Merge remote-tracking branch 'gitee/mymessagemanager' into appbase
This commit is contained in:
		| @@ -45,15 +45,17 @@ android { | |||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|     api fileTree(dir: 'libs', include: ['*.jar']) |     api fileTree(dir: 'libs', include: ['*.jar']) | ||||||
|     api 'cc.winboll.studio:libaes:15.9.2' |     api 'cc.winboll.studio:libaes:15.9.3' | ||||||
|     api 'cc.winboll.studio:libapputils:15.8.4' |     api 'cc.winboll.studio:libapputils:15.8.5' | ||||||
|     api 'cc.winboll.studio:libappbase:15.8.4' |     api 'cc.winboll.studio:libappbase:15.9.5' | ||||||
|      |      | ||||||
| 	api 'io.github.medyo:android-about-page:2.0.0' | 	api 'io.github.medyo:android-about-page:2.0.0' | ||||||
|     api 'com.github.getActivity:ToastUtils:10.5' |     api 'com.github.getActivity:ToastUtils:10.5' | ||||||
|     api 'com.jcraft:jsch:0.1.55' |     api 'com.jcraft:jsch:0.1.55' | ||||||
|     api 'org.jsoup:jsoup:1.13.1' |     api 'org.jsoup:jsoup:1.13.1' | ||||||
|     api 'com.squareup.okhttp3:okhttp:4.4.1' |     api 'com.squareup.okhttp3:okhttp:4.4.1' | ||||||
|  | 	 | ||||||
|  | 	api 'com.belerweb:pinyin4j:2.5.1' | ||||||
|      |      | ||||||
|     // 权限请求框架:https://github.com/getActivity/XXPermissions |     // 权限请求框架:https://github.com/getActivity/XXPermissions | ||||||
|     api 'com.github.getActivity:XXPermissions:18.63' |     api 'com.github.getActivity:XXPermissions:18.63' | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #Created by .winboll/winboll_app_build.gradle | #Created by .winboll/winboll_app_build.gradle | ||||||
| #Thu Jul 03 13:50:15 HKT 2025 | #Sun Aug 31 06:13:45 CST 2025 | ||||||
| stageCount=2 | stageCount=7 | ||||||
| libraryProject= | libraryProject= | ||||||
| baseVersion=15.3 | baseVersion=15.3 | ||||||
| publishVersion=15.3.1 | publishVersion=15.3.6 | ||||||
| buildCount=0 | buildCount=0 | ||||||
| baseBetaVersion=15.3.2 | baseBetaVersion=15.3.7 | ||||||
|   | |||||||
| @@ -1,5 +1,10 @@ | |||||||
| package cc.winboll.studio.mymessagemanager.activitys; | package cc.winboll.studio.mymessagemanager.activitys; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @Author ZhanGSKen&豆包大模型<zhangsken@188.com> | ||||||
|  |  * @Date 2025/08/30 14:32 | ||||||
|  |  * @Describe 联系人查询与短信发送窗口 | ||||||
|  |  */ | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.text.Editable; | import android.text.Editable; | ||||||
| import android.text.TextWatcher; | import android.text.TextWatcher; | ||||||
| @@ -11,13 +16,17 @@ import android.widget.RelativeLayout; | |||||||
| import android.widget.SimpleAdapter; | import android.widget.SimpleAdapter; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
| import android.widget.Toolbar; | import android.widget.Toolbar; | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.graphics.drawable.Drawable; | ||||||
|  |  | ||||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||||
| import cc.winboll.studio.mymessagemanager.R; | import cc.winboll.studio.mymessagemanager.R; | ||||||
| import cc.winboll.studio.mymessagemanager.activitys.ComposeSMSActivity; |  | ||||||
| import cc.winboll.studio.mymessagemanager.beans.PhoneBean; | import cc.winboll.studio.mymessagemanager.beans.PhoneBean; | ||||||
| import cc.winboll.studio.mymessagemanager.utils.PhoneUtil; | import cc.winboll.studio.mymessagemanager.utils.PhoneUtil; | ||||||
| import cc.winboll.studio.mymessagemanager.utils.SMSUtil; | import cc.winboll.studio.mymessagemanager.utils.SMSUtil; | ||||||
| import com.hjq.toast.ToastUtils; | import com.hjq.toast.ToastUtils; | ||||||
|  | import cc.winboll.studio.libappbase.LogUtils; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -26,166 +35,282 @@ import java.util.Map; | |||||||
| public class ComposeSMSActivity extends BaseActivity { | public class ComposeSMSActivity extends BaseActivity { | ||||||
|  |  | ||||||
|     public static String TAG = "ComposeSMSActivity"; |     public static String TAG = "ComposeSMSActivity"; | ||||||
|  |  | ||||||
|     public static String EXTRA_SMSBODY = "sms_body"; |     public static String EXTRA_SMSBODY = "sms_body"; | ||||||
|  |     private static final String MAP_NAME = "NAME"; | ||||||
|  |     private static final String MAP_PHONE = "PHONE"; | ||||||
|  |  | ||||||
|     static String MAP_NAME = "NAME"; |     private String mszSMSBody; | ||||||
|     static String MAP_PHONE = "PHONE"; |     private String mszScheme; | ||||||
|  |     private String mszPhoneTo; | ||||||
|     String mszSMSBody; |     private TextView mtvTOName; | ||||||
|     String mszScheme; |     private EditText metTONameSearch; | ||||||
|     String mszPhoneTo; |     private EditText metTO; | ||||||
|     EditText metTO; |     private EditText metSMSBody; | ||||||
|     EditText metSMSBody; |     private SimpleAdapter mSimpleAdapter; | ||||||
|     SimpleAdapter mSimpleAdapter; |     private List<Map<String, Object>> mAdapterData = new ArrayList<Map<String, Object>>(); | ||||||
|     List<Map<String,Object>> mAdapterData = new ArrayList<>(); |     private ListView mlvContracts; | ||||||
|     ListView mlvContracts; |     private List<PhoneBean> mListPhoneBeanContracts; | ||||||
|     List<PhoneBean> mListPhoneBeanContracts; |     private Toolbar mToolbar; | ||||||
|     Toolbar mToolbar; |     private AOHPCTCSeekBar mAOHPCTCSeekBar; | ||||||
|     AOHPCTCSeekBar mAOHPCTCSeekBar; |     private RelativeLayout mrlContracts; | ||||||
|     RelativeLayout mrlContracts; |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
|         setContentView(R.layout.activity_composesms); |         setContentView(R.layout.activity_composesms); | ||||||
|         mszSMSBody = getIntent().getStringExtra(EXTRA_SMSBODY); |  | ||||||
|         mszScheme = getIntent().getData().getScheme(); |         // 初始化Intent数据(增加空判断,避免NullPointerException) | ||||||
|         mszPhoneTo = getIntent().getData().getSchemeSpecificPart(); |         Intent intent = getIntent(); | ||||||
|         if (!mszScheme.equals("smsto")) { |         if (intent != null) { | ||||||
|             // 其他方式未支持就退出 |             mszSMSBody = intent.getStringExtra(EXTRA_SMSBODY); | ||||||
|             finish(); |             if (intent.getData() != null) { | ||||||
|  |                 mszScheme = intent.getData().getScheme(); | ||||||
|  |                 mszPhoneTo = intent.getData().getSchemeSpecificPart(); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         // 初始化视图 |  | ||||||
|  |         // 校验启动方式,非smsto则退出 | ||||||
|  |         if (mszScheme == null || !"smsto".equals(mszScheme)) { | ||||||
|  |             ToastUtils.show("不支持的启动方式"); | ||||||
|  |             finish(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         initView(); |         initView(); | ||||||
|         // 设置适配器 |         initAdapter(null); // 初始加载所有联系人 | ||||||
|         initAdapter(); |         setListViewPrePositionByPhone(); | ||||||
|         // 设置搜索到的匹配位置 |  | ||||||
|         setListViewPrePosition(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     private void initView() { | ||||||
|     // 初始化视图 |  | ||||||
|     // |  | ||||||
|     void initView() { |  | ||||||
|         //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame); |  | ||||||
|  |  | ||||||
|         // 初始化标题栏 |         // 初始化标题栏 | ||||||
|         mToolbar = findViewById(R.id.activitycomposesmsASupportToolbar1); |         mToolbar = (Toolbar) findViewById(R.id.activitycomposesmsASupportToolbar1); | ||||||
|         mToolbar.setSubtitle(getString(R.string.activity_name_composesms)); |         mToolbar.setSubtitle(getString(R.string.activity_name_composesms)); | ||||||
|         setActionBar(mToolbar); |         setActionBar(mToolbar); | ||||||
|  |  | ||||||
|         // 初始化联系人栏目框 |         // 初始化联系人姓名显示和搜索栏 | ||||||
|         mrlContracts = findViewById(R.id.activitycomposesmsRelativeLayout1); |         mtvTOName = (TextView) findViewById(R.id.activitycomposesmsTextView2); | ||||||
|         //mrlContracts.setBackground(drawableFrame); |         mrlContracts = (RelativeLayout) findViewById(R.id.activitycomposesmsRelativeLayout1); | ||||||
|  |         metTONameSearch = (EditText) findViewById(R.id.activitycomposesmsEditText2); | ||||||
|  |  | ||||||
|         // 初始化联系人列表 |         // 姓名搜索框文本变化监听 | ||||||
|         mlvContracts = findViewById(R.id.activitycomposesmsListView1); |         metTONameSearch.addTextChangedListener(new TextWatcher() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onTextChanged(CharSequence s, int start, int before, int count) { | ||||||
|  | 					metTO.setText(""); // 清空号码输入框,避免冲突 | ||||||
|  | 					String input = s == null ? "" : s.toString().trim(); | ||||||
|  | 					if (input.isEmpty()) { | ||||||
|  | 						initAdapter(null); // 空搜索时显示所有联系人 | ||||||
|  | 					} else { | ||||||
|  | 						setListViewPrePositionByName(); // 按姓名搜索 | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|         // 初始化联系人输入框 | 				@Override | ||||||
|         metTO = findViewById(R.id.activitycomposesmsEditText1); | 				public void beforeTextChanged(CharSequence s, int start, int count, int after) { | ||||||
|         metTO.setText(mszPhoneTo); | 					// 无操作 | ||||||
|         metTO.addTextChangedListener(new TextWatcher() {            | 				} | ||||||
|                 @Override   |  | ||||||
|                 public void onTextChanged(CharSequence s, int start, int before, int count) { |  | ||||||
|                     setListViewPrePosition(); |  | ||||||
|                 } |  | ||||||
|                 @Override   |  | ||||||
|                 public void beforeTextChanged(CharSequence s, int start, int count, int after) { |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 @Override   | 				@Override | ||||||
|                 public void afterTextChanged(Editable s) { | 				public void afterTextChanged(Editable s) { | ||||||
|                 } | 					// 无操作 | ||||||
|             }); | 				} | ||||||
|  |  | ||||||
|         // 初始化发送拉动控件 |  | ||||||
|         mAOHPCTCSeekBar = findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1); |  | ||||||
|         mAOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message)); |  | ||||||
|         mAOHPCTCSeekBar.setThumbOffset(20); |  | ||||||
|         mAOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() { |  | ||||||
|                 @Override |  | ||||||
|                 public void onOHPCommit() { |  | ||||||
|                     // 空号码不发送 |  | ||||||
|                     mszPhoneTo = metTO.getText().toString(); |  | ||||||
|                     if (mszPhoneTo.trim().equals("")) { |  | ||||||
|                         ToastUtils.show("没有设置接收号码。"); |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|                     // 空消息不发送 |  | ||||||
|                     mszSMSBody = metSMSBody.getText().toString(); |  | ||||||
|                     if (mszSMSBody.equals("")) { |  | ||||||
|                         ToastUtils.show("没有消息内容可发送。"); |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|                     // 发送消息 |  | ||||||
|                     if (SMSUtil.sendMessageByInterface2(ComposeSMSActivity.this, mszPhoneTo, mszSMSBody)) { |  | ||||||
|                         ComposeSMSActivity.this.finish(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 			}); | 			}); | ||||||
|  |  | ||||||
|         // 初始化提示框 |         // 初始化联系人列表 | ||||||
|         TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.viewsmssendpart1TextView1); |         mlvContracts = (ListView) findViewById(R.id.activitycomposesmsListView1); | ||||||
|  |  | ||||||
|  |         // 初始化号码输入框(核心:优化文本变化监听逻辑) | ||||||
|  |         metTO = (EditText) findViewById(R.id.activitycomposesmsEditText1); | ||||||
|  |         if (mszPhoneTo != null) { | ||||||
|  |             metTO.setText(mszPhoneTo); | ||||||
|  |         } | ||||||
|  |         metTO.addTextChangedListener(new TextWatcher() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onTextChanged(CharSequence s, int start, int before, int count) { | ||||||
|  | 					mtvTOName.setText(""); // 清空姓名显示 | ||||||
|  | 					String inputPhone = s == null ? "" : s.toString().trim(); | ||||||
|  |  | ||||||
|  | 					if (inputPhone.isEmpty()) { | ||||||
|  | 						// 输入为空时,显示所有联系人 | ||||||
|  | 						initAdapter(null); | ||||||
|  | 					} else { | ||||||
|  | 						// 输入非空时,按号码搜索并更新列表(无结果则清空) | ||||||
|  | 						filterListByPhone(inputPhone); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				@Override | ||||||
|  | 				public void beforeTextChanged(CharSequence s, int start, int count, int after) { | ||||||
|  | 					// 无操作 | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				@Override | ||||||
|  | 				public void afterTextChanged(Editable s) { | ||||||
|  | 					// 无操作 | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  |         // 初始化发送控件 | ||||||
|  |         mAOHPCTCSeekBar = (AOHPCTCSeekBar) findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1); | ||||||
|  |         Drawable thumbDrawable = getResources().getDrawable(R.drawable.ic_message); // Java 7兼容写法 | ||||||
|  |         mAOHPCTCSeekBar.setThumb(thumbDrawable); | ||||||
|  |         mAOHPCTCSeekBar.setThumbOffset(20); | ||||||
|  |         mAOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onOHPCommit() { | ||||||
|  | 					sendSMS(); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  |         // 初始化短信内容输入框 | ||||||
|  |         TextView tvAOHPCTCSeekBarMSG = (TextView) findViewById(R.id.viewsmssendpart1TextView1); | ||||||
|         tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg); |         tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg); | ||||||
|  |         metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1); | ||||||
|         // 初始化发送消息框 |         if (mszSMSBody != null) { | ||||||
|         metSMSBody = findViewById(R.id.viewsmssendpart1EditText1); |             metSMSBody.setText(mszSMSBody); | ||||||
|         //metSMSBody.setBackground(drawableFrame); |         } | ||||||
|         metSMSBody.setText(mszSMSBody); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // 核心优化:根据输入号码筛选列表(无结果则显示空列表) | ||||||
|     // 设置搜索到的匹配位置 |     private void filterListByPhone(String inputPhone) { | ||||||
|     // |         PhoneUtil phoneUtil = new PhoneUtil(this); | ||||||
|     void setListViewPrePosition() { |         List<PhoneBean> allContacts = phoneUtil.getPhoneList(); | ||||||
|         int nPrePosition = getContractsDataPrePosition(metTO.getText().toString()); |         List<PhoneBean> matchedContacts = new ArrayList<PhoneBean>(); | ||||||
|         mlvContracts.setSelected(false); |  | ||||||
|         mlvContracts.setSelection(nPrePosition); |         // 遍历所有联系人,匹配包含输入号码的联系人 | ||||||
|  |         for (PhoneBean contact : allContacts) { | ||||||
|  |             if (contact.getTelPhone().contains(inputPhone)  | ||||||
|  |                 || phoneUtil.isTheSamePhoneNumber(contact.getTelPhone(), inputPhone)) { | ||||||
|  |                 matchedContacts.add(contact); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         LogUtils.d(TAG, "号码搜索:输入'" + inputPhone + "', 匹配" + matchedContacts.size() + "个结果"); | ||||||
|  |  | ||||||
|  |         // 用筛选结果更新列表(无结果则传入空列表) | ||||||
|  |         initAdapter(matchedContacts.isEmpty() ? new ArrayList<PhoneBean>() : matchedContacts); | ||||||
|  |  | ||||||
|  |         // 定位到第一个匹配项(如果有) | ||||||
|  |         if (!matchedContacts.isEmpty()) { | ||||||
|  |             mlvContracts.setSelection(0); | ||||||
|  |             mtvTOName.setText(matchedContacts.get(0).getName()); | ||||||
|  |         } else { | ||||||
|  |             mtvTOName.setText(""); // 无结果时清空姓名显示 | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // 根据姓名搜索联系人 | ||||||
|     // 返回搜索到的匹配位置 |     private void setListViewPrePositionByName() { | ||||||
|     // |         String searchName = metTONameSearch.getText().toString().trim(); | ||||||
|     int getContractsDataPrePosition(String szPhone) { |         PhoneUtil phoneUtil = new PhoneUtil(this); | ||||||
|  |         List<PhoneBean> matchedContacts = phoneUtil.getPhonesByName(searchName); | ||||||
|  |         initAdapter(matchedContacts); | ||||||
|  |         if (!matchedContacts.isEmpty()) { | ||||||
|  |             mlvContracts.setSelection(0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 初始定位号码对应的联系人 | ||||||
|  |     private void setListViewPrePositionByPhone() { | ||||||
|  |         String inputPhone = metTO.getText().toString().trim(); | ||||||
|  |         if (inputPhone.isEmpty()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         filterListByPhone(inputPhone); // 复用筛选逻辑 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 获取号码匹配的位置(兼容旧逻辑) | ||||||
|  |     private int getContractsDataPrePositionByPhone(String szPhone) { | ||||||
|  |         if (mListPhoneBeanContracts == null || mListPhoneBeanContracts.isEmpty()) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|         for (int i = 0; i < mListPhoneBeanContracts.size(); i++) { |         for (int i = 0; i < mListPhoneBeanContracts.size(); i++) { | ||||||
|             if (mListPhoneBeanContracts.get(i).getTelPhone().compareTo(szPhone) > -1) { |             PhoneBean bean = mListPhoneBeanContracts.get(i); | ||||||
|  |             if (bean.getTelPhone().compareTo(szPhone) >= 0) { | ||||||
|                 return i; |                 return i; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|         } |         } | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // 获取姓名匹配的位置(兼容旧逻辑) | ||||||
|     // 初始化适配器 |     private int getContractsDataPrePositionByName(String szName) { | ||||||
|     // |         if (mListPhoneBeanContracts == null || mListPhoneBeanContracts.isEmpty()) { | ||||||
|     void initAdapter() { |             return 0; | ||||||
|         // 初始化联系人数据适配器 |  | ||||||
|         mAdapterData = new ArrayList<>(); |  | ||||||
|         // 读取联系人数据 |  | ||||||
|         PhoneUtil phoneUtils = new PhoneUtil(this); |  | ||||||
|         mListPhoneBeanContracts = phoneUtils.getPhoneList(); |  | ||||||
|         // 映射联系人数据给适配器数据对象 |  | ||||||
|         for (int i = 0;i < mListPhoneBeanContracts.size();i++) { |  | ||||||
|             Map<String,Object> map =new HashMap<>(); |  | ||||||
|             map.put(MAP_NAME, mListPhoneBeanContracts.get(i).getName()); |  | ||||||
|             map.put(MAP_PHONE, mListPhoneBeanContracts.get(i).getTelPhone()); |  | ||||||
|             mAdapterData.add(map); |  | ||||||
|         } |         } | ||||||
|         // 绑定适配器与数据 |         for (int i = 0; i < mListPhoneBeanContracts.size(); i++) { | ||||||
|         mSimpleAdapter = new SimpleAdapter(ComposeSMSActivity.this, mAdapterData, R.layout.listview_contracts |             if (mListPhoneBeanContracts.get(i).getName().startsWith(szName)) { | ||||||
|                                            , new String[]{MAP_NAME, MAP_PHONE} |                 return i; | ||||||
|                                            , new int[]{R.id.listviewcontractsTextView1, R.id.listviewcontractsTextView2}); |             } | ||||||
|         mSimpleAdapter.setDropDownViewResource(R.layout.listview_contracts); |         } | ||||||
|         mlvContracts.setAdapter(mSimpleAdapter); |         return 0; | ||||||
|         mlvContracts.setOnItemClickListener(new ListView.OnItemClickListener() { |     } | ||||||
|  |  | ||||||
|                 @Override |     // 初始化或更新列表适配器 | ||||||
|                 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |     private void initAdapter(List<PhoneBean> initData) { | ||||||
|                     metTO.setText(mAdapterData.get(position).get(MAP_PHONE).toString()); |         mAdapterData.clear(); // 清空旧数据 | ||||||
|  |         final PhoneUtil phoneUtil = new PhoneUtil(this); | ||||||
|  |  | ||||||
|                 } |         // 确定数据源:传入的筛选数据或所有联系人 | ||||||
|             }); |         if (initData != null) { | ||||||
|  |             mListPhoneBeanContracts = initData; | ||||||
|  |         } else { | ||||||
|  |             mListPhoneBeanContracts = phoneUtil.getPhoneList(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 转换数据为SimpleAdapter所需格式 | ||||||
|  |         if (mListPhoneBeanContracts != null) { | ||||||
|  |             for (PhoneBean bean : mListPhoneBeanContracts) { | ||||||
|  |                 Map<String, Object> map = new HashMap<String, Object>(); | ||||||
|  |                 map.put(MAP_NAME, bean.getName()); | ||||||
|  |                 map.put(MAP_PHONE, bean.getTelPhone()); | ||||||
|  |                 mAdapterData.add(map); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 初始化或更新适配器 | ||||||
|  |         if (mSimpleAdapter == null) { | ||||||
|  |             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 AdapterView.OnItemClickListener() { | ||||||
|  | 					@Override | ||||||
|  | 					public void onItemClick(AdapterView<?> parent, View view, int position, long id) { | ||||||
|  | 						if (position < mAdapterData.size()) { | ||||||
|  | 							String phone = mAdapterData.get(position).get(MAP_PHONE).toString(); | ||||||
|  | 							metTO.setText(phone); | ||||||
|  | 							mtvTOName.setText(phoneUtil.getNameByPhone(phone)); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				}); | ||||||
|  |         } else { | ||||||
|  |             mSimpleAdapter.notifyDataSetChanged(); // 数据更新时通知适配器 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 发送短信逻辑 | ||||||
|  |     private void sendSMS() { | ||||||
|  |         String phoneTo = metTO.getText().toString().trim(); | ||||||
|  |         if (phoneTo.isEmpty()) { | ||||||
|  |             ToastUtils.show("没有设置接收号码。"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         String smsBody = metSMSBody.getText().toString().trim(); | ||||||
|  |         if (smsBody.isEmpty()) { | ||||||
|  |             ToastUtils.show("没有消息内容可发送。"); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (SMSUtil.sendMessageByInterface2(ComposeSMSActivity.this, phoneTo, smsBody)) { | ||||||
|  |             finish(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,11 +4,16 @@ import android.content.BroadcastReceiver; | |||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.IntentFilter; | import android.content.IntentFilter; | ||||||
|  | import android.os.Build; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.os.Handler; | import android.os.Handler; | ||||||
| import android.os.Message; | import android.os.Message; | ||||||
|  | import android.view.View; | ||||||
|  | import android.view.ViewTreeObserver; | ||||||
|  | import android.view.inputmethod.InputMethodManager; | ||||||
| import android.widget.AbsListView; | import android.widget.AbsListView; | ||||||
| import android.widget.EditText; | import android.widget.EditText; | ||||||
|  | import android.widget.LinearLayout; | ||||||
| import android.widget.ScrollView; | import android.widget.ScrollView; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
| @@ -16,19 +21,17 @@ import android.widget.Toolbar; | |||||||
| import androidx.localbroadcastmanager.content.LocalBroadcastManager; | import androidx.localbroadcastmanager.content.LocalBroadcastManager; | ||||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||||
| import cc.winboll.studio.mymessagemanager.R; | import cc.winboll.studio.mymessagemanager.R; | ||||||
| import cc.winboll.studio.mymessagemanager.activitys.SMSActivity; |  | ||||||
| import cc.winboll.studio.mymessagemanager.adapters.SMSArrayAdapter; | import cc.winboll.studio.mymessagemanager.adapters.SMSArrayAdapter; | ||||||
| import cc.winboll.studio.mymessagemanager.utils.AddressUtils; | import cc.winboll.studio.mymessagemanager.utils.AddressUtils; | ||||||
| import cc.winboll.studio.mymessagemanager.utils.SMSUtil; | import cc.winboll.studio.mymessagemanager.utils.SMSUtil; | ||||||
| import cc.winboll.studio.mymessagemanager.utils.ViewUtil; | import cc.winboll.studio.mymessagemanager.utils.ViewUtil; | ||||||
|  | import cc.winboll.studio.mymessagemanager.views.BottomPositionFixedScrollView; | ||||||
| import cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView; | import cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView; | ||||||
| import java.lang.ref.WeakReference; | import java.lang.ref.WeakReference; | ||||||
|  |  | ||||||
| public class SMSActivity extends BaseActivity { | public class SMSActivity extends BaseActivity { | ||||||
|     public static String TAG = "SMSActivity"; |     public static String TAG = "SMSActivity"; | ||||||
|  |  | ||||||
|     public static final String ACTION_NOTIFY_SMS_CHANGED = "cc.winboll.studio.mymessagemanager.activitys.SMSActivity.ACTION_NOTIFY_SMS_CHANGED"; |     public static final String ACTION_NOTIFY_SMS_CHANGED = "cc.winboll.studio.mymessagemanager.activitys.SMSActivity.ACTION_NOTIFY_SMS_CHANGED"; | ||||||
|  |  | ||||||
|     public static final String EXTRA_PHONE = "Phone"; |     public static final String EXTRA_PHONE = "Phone"; | ||||||
|     final static int MSG_SET_FOCUS = 0; |     final static int MSG_SET_FOCUS = 0; | ||||||
|  |  | ||||||
| @@ -36,10 +39,11 @@ public class SMSActivity extends BaseActivity { | |||||||
|     Toolbar mToolbar; |     Toolbar mToolbar; | ||||||
|     String mszPhoneTo; |     String mszPhoneTo; | ||||||
|     SMSArrayAdapter mSMSArrayAdapter; |     SMSArrayAdapter mSMSArrayAdapter; | ||||||
|     ScrollView mScrollView; |     BottomPositionFixedScrollView mScrollView1; | ||||||
|     EditText metSMSBody; |     EditText metSMSBody; | ||||||
|     SMSActivityBroadcastReceiver mSMSActivityBroadcastReceiver; |     SMSActivityBroadcastReceiver mSMSActivityBroadcastReceiver; | ||||||
|     Handler mSetFocusHandler; |     Handler mSetFocusHandler; | ||||||
|  |     private boolean isImeVisible = false; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
| @@ -47,45 +51,90 @@ public class SMSActivity extends BaseActivity { | |||||||
|         setContentView(R.layout.activity_sms); |         setContentView(R.layout.activity_sms); | ||||||
|  |  | ||||||
|         initView(); |         initView(); | ||||||
| 		mSetFocusHandler = new MyHandler(SMSActivity.this); |  | ||||||
|         scrollScrollView(); |         scrollScrollView(); | ||||||
|  |         setupImeStatusListener(); | ||||||
|  |  | ||||||
|         // 每隔一定时间设置输入框获得焦点 |         // 新增:监听窗口加载完成,触发mScrollView1滚动到底部 | ||||||
|         // |         setupScrollToBottomAfterWindowLoaded(); | ||||||
|         new Thread() { |  | ||||||
|             @Override |  | ||||||
|             public void run() { |  | ||||||
|                 while (true) { |  | ||||||
|                     try { |  | ||||||
|                         Thread.sleep(1500); |  | ||||||
|                     } catch (InterruptedException e) {} |  | ||||||
|                     Message message = mSetFocusHandler.obtainMessage(MSG_SET_FOCUS); |  | ||||||
|                     mSetFocusHandler.sendMessage(message); |  | ||||||
|                 } |  | ||||||
|             }}.start(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // 新增:窗口加载完成后让mScrollView1滚动到底部 | ||||||
|     // 设置输入框获得焦点的类 |     private void setupScrollToBottomAfterWindowLoaded() { | ||||||
| 	// |         final View rootView = findViewById(android.R.id.content); | ||||||
| 	static class MyHandler extends Handler { |         // 监听根布局绘制完成(窗口加载完成的标志) | ||||||
| 		WeakReference<SMSActivity> mActivity;   |         rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { | ||||||
| 		MyHandler(SMSActivity activity) {   | 				@Override | ||||||
| 			mActivity = new WeakReference<SMSActivity>(activity);   | 				public void onGlobalLayout() { | ||||||
| 		} | 					// 滚动到底部 | ||||||
|         public void handleMessage(Message msg) { | 					mScrollView1.post(new Runnable() { | ||||||
| 			SMSActivity theActivity = mActivity.get(); | 							@Override | ||||||
|             switch (msg.what) { | 							public void run() { | ||||||
|                 case MSG_SET_FOCUS: | 								mScrollView1.fullScroll(ScrollView.FOCUS_DOWN); | ||||||
|                     theActivity.metSMSBody.setFocusable(true); | 							} | ||||||
|                     theActivity.metSMSBody.requestFocus(); | 						}); | ||||||
|                     break; |  | ||||||
|                 default: | 					// 移除监听,避免重复触发 | ||||||
|                     break; | 					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { | ||||||
|             } | 						rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this); | ||||||
|             super.handleMessage(msg); | 					} else { | ||||||
|         } | 						rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this); | ||||||
| 	} | 					} | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void setupImeStatusListener() { | ||||||
|  |         final View rootView = findViewById(android.R.id.content); | ||||||
|  |         rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onGlobalLayout() { | ||||||
|  | 					int rootViewHeight = rootView.getHeight(); | ||||||
|  | 					int screenHeight = getResources().getDisplayMetrics().heightPixels; | ||||||
|  | 					int imeThreshold = dp2px(200); | ||||||
|  |  | ||||||
|  | 					boolean currentImeVisible = (screenHeight - rootViewHeight) > imeThreshold; | ||||||
|  |  | ||||||
|  | 					if (currentImeVisible != isImeVisible) { | ||||||
|  | 						isImeVisible = currentImeVisible; | ||||||
|  | 						setupScrollView1Height(); | ||||||
|  | 						if (!isImeVisible) { | ||||||
|  | 							metSMSBody.clearFocus(); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { | ||||||
|  | 						rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this); | ||||||
|  | 					} else { | ||||||
|  | 						rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this); | ||||||
|  | 					} | ||||||
|  | 					setupImeStatusListener(); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private int dp2px(int dp) { | ||||||
|  |         return (int) (dp * getResources().getDisplayMetrics().density + 0.5f); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /*static class MyHandler extends Handler { | ||||||
|  | 	 WeakReference<SMSActivity> mActivity; | ||||||
|  | 	 MyHandler(SMSActivity activity) { | ||||||
|  | 	 mActivity = new WeakReference<SMSActivity>(activity); | ||||||
|  | 	 } | ||||||
|  | 	 public void handleMessage(Message msg) { | ||||||
|  | 	 SMSActivity theActivity = mActivity.get(); | ||||||
|  | 	 switch (msg.what) { | ||||||
|  | 	 case MSG_SET_FOCUS: | ||||||
|  | 	 theActivity.metSMSBody.setFocusable(true); | ||||||
|  | 	 theActivity.metSMSBody.requestFocus(); | ||||||
|  | 	 theActivity.setupScrollView1Height(); | ||||||
|  | 	 break; | ||||||
|  | 	 default: | ||||||
|  | 	 break; | ||||||
|  | 	 } | ||||||
|  | 	 super.handleMessage(msg); | ||||||
|  | 	 } | ||||||
|  | 	 }*/ | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void onDestroy() { |     protected void onDestroy() { | ||||||
| @@ -94,135 +143,130 @@ public class SMSActivity extends BaseActivity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     void initView() { |     void initView() { | ||||||
|         // 发送端空号码退出 |  | ||||||
|         mszPhoneTo = getIntent().getStringExtra(EXTRA_PHONE); |         mszPhoneTo = getIntent().getStringExtra(EXTRA_PHONE); | ||||||
|         if (mszPhoneTo == null || mszPhoneTo.trim().equals("")) { |         if (mszPhoneTo == null || mszPhoneTo.trim().equals("")) { | ||||||
|             finish(); |             finish(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 初始化标题栏 |         mToolbar = (Toolbar) findViewById(R.id.activitysmsASupportToolbar1); | ||||||
|         mToolbar = findViewById(R.id.activitysmsASupportToolbar1); |  | ||||||
|         mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + AddressUtils.getFormattedAddress(mszPhoneTo) + " >"); |         mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + AddressUtils.getFormattedAddress(mszPhoneTo) + " >"); | ||||||
|         setActionBar(mToolbar); |         setActionBar(mToolbar); | ||||||
|  |  | ||||||
|         // 初始化滚动窗口 |         mScrollView1 = (BottomPositionFixedScrollView) findViewById(R.id.activitysmsScrollView1); | ||||||
|         mScrollView = findViewById(R.id.activitysmsinphoneScrollView1); |  | ||||||
|  |  | ||||||
|         // 初始化发送消息框 |         metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1); | ||||||
|         //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame); |         metSMSBody.setOnClickListener(new View.OnClickListener() { | ||||||
|         metSMSBody = findViewById(R.id.viewsmssendpart1EditText1); | 				@Override | ||||||
|         //metSMSBody.setBackground(drawableFrame); | 				public void onClick(View v) { | ||||||
|  | 					setupScrollView1Height(); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |         metSMSBody.setOnFocusChangeListener(new View.OnFocusChangeListener() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onFocusChange(View v, boolean hasFocus) { | ||||||
|  | 					setupScrollView1Height(); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|         // 初始化发送拉动控件 |         final AOHPCTCSeekBar aOHPCTCSeekBar = (AOHPCTCSeekBar) findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1); | ||||||
|         final AOHPCTCSeekBar aOHPCTCSeekBar = findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1); |  | ||||||
|         aOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message)); |         aOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message)); | ||||||
|         aOHPCTCSeekBar.setThumbOffset(20); |         aOHPCTCSeekBar.setThumbOffset(20); | ||||||
|         aOHPCTCSeekBar.setOnOHPCListener( |         aOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() { | ||||||
|             new AOHPCTCSeekBar.OnOHPCListener(){ | 				@Override | ||||||
|                 @Override | 				public void onOHPCommit() { | ||||||
|                 public void onOHPCommit() { | 					sendSMS(); | ||||||
|                     //Toast.makeText(getApplication(), "Send", Toast.LENGTH_SHORT).show(); | 				} | ||||||
|                     sendSMS(); | 			}); | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         // 初始化提示框 |         TextView tvAOHPCTCSeekBarMSG = (TextView) findViewById(R.id.viewsmssendpart1TextView1); | ||||||
|         TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.viewsmssendpart1TextView1); |  | ||||||
|         tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg); |         tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg); | ||||||
|  |  | ||||||
|         mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsinphoneListView1); |         mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsSMSListViewForScrollView1); | ||||||
|  |  | ||||||
|         // 准备数据 |  | ||||||
|         mSMSArrayAdapter = new SMSArrayAdapter(SMSActivity.this, mszPhoneTo); |         mSMSArrayAdapter = new SMSArrayAdapter(SMSActivity.this, mszPhoneTo); | ||||||
|         mlvSMS.setAdapter(mSMSArrayAdapter); |         mlvSMS.setAdapter(mSMSArrayAdapter); | ||||||
|  |  | ||||||
|         // 设置短信列表滚动到底部就取消已发送的通知消息 |  | ||||||
|         // |  | ||||||
|         mlvSMS.setOnScrollListener(new AbsListView.OnScrollListener() { |         mlvSMS.setOnScrollListener(new AbsListView.OnScrollListener() { | ||||||
|                 @Override | 				@Override | ||||||
|                 public void onScrollStateChanged(AbsListView view, int scrollState) { | 				public void onScrollStateChanged(AbsListView view, int scrollState) {} | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 @Override | 				@Override | ||||||
|                 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { | 				public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { | ||||||
|                     if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) { | 					if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) { | ||||||
|                         // 滑动到了底部 | 						mSMSArrayAdapter.cancelMessageNotification(); | ||||||
|                         mSMSArrayAdapter.cancelMessageNotification(); | 					} | ||||||
|                     } | 				} | ||||||
|                 } | 			}); | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         mSMSActivityBroadcastReceiver = new SMSActivityBroadcastReceiver(); |         mSMSActivityBroadcastReceiver = new SMSActivityBroadcastReceiver(); | ||||||
|         IntentFilter intentFilter = new IntentFilter(); |         IntentFilter intentFilter = new IntentFilter(ACTION_NOTIFY_SMS_CHANGED); | ||||||
|         intentFilter.addAction(ACTION_NOTIFY_SMS_CHANGED); |  | ||||||
|         LocalBroadcastManager.getInstance(this).registerReceiver(mSMSActivityBroadcastReceiver, intentFilter); |         LocalBroadcastManager.getInstance(this).registerReceiver(mSMSActivityBroadcastReceiver, intentFilter); | ||||||
|  |  | ||||||
|         /*SMSView mSMSView = findViewById(R.id.viewsmssendSMSView1); |  | ||||||
|         mSMSView.setSMSType(SMSView.SMSType.SEND);*/ |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     private void setupScrollView1Height() { | ||||||
|     // 更新信息列表 |         mScrollView1.postDelayed(new Runnable() { | ||||||
|     // | 				@Override | ||||||
|  | 				public void run() { | ||||||
|  | 					final ScrollView scrollView2 = (ScrollView) findViewById(R.id.activitysmsScrollView2); | ||||||
|  | 					final BottomPositionFixedScrollView scrollView1 = (BottomPositionFixedScrollView) findViewById(R.id.activitysmsScrollView1); | ||||||
|  | 					final View includeView = findViewById(R.id.activitysmsinclude1); | ||||||
|  |  | ||||||
|  | 					scrollView2.post(new Runnable() { | ||||||
|  | 							@Override | ||||||
|  | 							public void run() { | ||||||
|  | 								int scrollView2Height = scrollView2.getHeight(); | ||||||
|  | 								int includeHeight = includeView.getHeight(); | ||||||
|  | 								int targetHeight = Math.max(scrollView2Height - includeHeight, 0); | ||||||
|  |  | ||||||
|  | 								LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) scrollView1.getLayoutParams(); | ||||||
|  | 								params.height = targetHeight; | ||||||
|  | 								scrollView1.setLayoutParams(params); | ||||||
|  | 							} | ||||||
|  | 						}); | ||||||
|  | 				} | ||||||
|  | 			}, 100); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void updateSMSView() { |     public void updateSMSView() { | ||||||
|         mSMSArrayAdapter.reLoadSMSList(SMSActivity.this, mszPhoneTo); |         mSMSArrayAdapter.reLoadSMSList(SMSActivity.this, mszPhoneTo); | ||||||
|         mSMSArrayAdapter.notifyDataSetChanged(); |         mSMSArrayAdapter.notifyDataSetChanged(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |  | ||||||
|     // 滚动消息文本框 |  | ||||||
|     // |  | ||||||
|     void scrollScrollView() { |     void scrollScrollView() { | ||||||
|  |         ViewUtil.scrollScrollView(mScrollView1); | ||||||
|         ViewUtil.scrollScrollView(mScrollView); |  | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |  | ||||||
|     // 发送短信 |  | ||||||
|     // |  | ||||||
|     void sendSMS() { |     void sendSMS() { | ||||||
|         // 空消息不发送 |  | ||||||
|         String szSMSBody = metSMSBody.getText().toString(); |         String szSMSBody = metSMSBody.getText().toString(); | ||||||
|         if (szSMSBody.equals("")) { |         if (szSMSBody.equals("")) { | ||||||
|             Toast.makeText(getApplication(), "没有消息内容可发送。", Toast.LENGTH_SHORT).show(); |             Toast.makeText(getApplication(), "没有消息内容可发送。", Toast.LENGTH_SHORT).show(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 发送短信 |  | ||||||
|         if (SMSUtil.sendMessageByInterface2(this, mszPhoneTo, szSMSBody)) { |         if (SMSUtil.sendMessageByInterface2(this, mszPhoneTo, szSMSBody)) { | ||||||
|             metSMSBody.setText(""); |             metSMSBody.setText(""); | ||||||
|             new Handler().postDelayed(new Runnable(){ |             metSMSBody.clearFocus(); | ||||||
|                     @Override |             new Handler().postDelayed(new Runnable() { | ||||||
|                     public void run() { | 					@Override | ||||||
|                         updateSMSView(); | 					public void run() { | ||||||
|                         ViewUtil.scrollScrollView(mScrollView); | 						updateSMSView(); | ||||||
|                     } | 						ViewUtil.scrollScrollView(mScrollView1); | ||||||
|                 }, 1000); | 					} | ||||||
|  | 				}, 1000); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     class SMSActivityBroadcastReceiver extends BroadcastReceiver { |     class SMSActivityBroadcastReceiver extends BroadcastReceiver { | ||||||
|  |         public SMSActivityBroadcastReceiver() {} | ||||||
|         public SMSActivityBroadcastReceiver() { |  | ||||||
|             //LogUtils.d(TAG, "SMSActivityBroadcastReceiver()"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
|         public void onReceive(Context context, Intent intent) { |         public void onReceive(Context context, Intent intent) { | ||||||
|             switch (intent.getAction()) { |             if (ACTION_NOTIFY_SMS_CHANGED.equals(intent.getAction())) { | ||||||
|                 case ACTION_NOTIFY_SMS_CHANGED : |                 updateSMSView(); | ||||||
|                     //Toast.makeText(context, "ACTION_NOTIFY_SMS_CHANGED", Toast.LENGTH_SHORT).show(); |                 ViewUtil.scrollScrollView(mScrollView1); | ||||||
|                     updateSMSView(); |             } else { | ||||||
|                     ViewUtil.scrollScrollView(mScrollView); |                 throw new IllegalStateException("Unexpected value: " + intent.getAction()); | ||||||
|                     //LogUtils.d(TAG, "ACTION_NOTIFY_SMS_CHANGED"); |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     throw new IllegalStateException("Unexpected value: " + intent.getAction()); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| package cc.winboll.studio.mymessagemanager.utils; | package cc.winboll.studio.mymessagemanager.utils; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @Author ZhanGSKen<zhangsken@188.com> |  * @Author ZhanGSKen&豆包大模型<zhangsken@188.com> | ||||||
|  * @Date 2024/07/19 14:30:57 |  * @Date 2025/08/30 14:32 | ||||||
|  * @Describe 手机联系人工具类 |  * @Describe 手机联系人工具类 | ||||||
|  */ |  */ | ||||||
| import android.content.ContentResolver; | import android.content.ContentResolver; | ||||||
| @@ -12,6 +12,12 @@ import android.net.Uri; | |||||||
| import android.provider.ContactsContract; | import android.provider.ContactsContract; | ||||||
| import cc.winboll.studio.libappbase.LogUtils; | import cc.winboll.studio.libappbase.LogUtils; | ||||||
| import cc.winboll.studio.mymessagemanager.beans.PhoneBean; | 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.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.Comparator; | import java.util.Comparator; | ||||||
| @@ -38,28 +44,137 @@ public class PhoneUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // 读取所有联系人 |     // 读取所有联系人 | ||||||
|     // |  | ||||||
|     public List<PhoneBean> getPhoneList() { |     public List<PhoneBean> getPhoneList() { | ||||||
|         List<PhoneBean> listPhoneBean = new ArrayList<>(); |         List<PhoneBean> listPhoneBean = new ArrayList<>(); | ||||||
|         ContentResolver cr = mContext.getContentResolver(); |         ContentResolver cr = mContext.getContentResolver(); | ||||||
|         Cursor cursor = cr.query(mUriPhoneContent, new String[]{NUMBER, DISPLAY_NAME}, null, null, null); |         Cursor cursor = cr.query(mUriPhoneContent, new String[]{NUMBER, DISPLAY_NAME}, null, null, null); | ||||||
|         while (cursor.moveToNext()) { |         if (cursor != null) { | ||||||
| 			PhoneBean phoneBean = new PhoneBean(cursor.getString(1), cursor.getString(0).replaceAll("\\s", "")); |             while (cursor.moveToNext()) { | ||||||
| 			listPhoneBean.add(phoneBean); |                 // 去除号码中的空格 | ||||||
|  |                 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<PhoneBean>() { |         Collections.sort(listPhoneBean, new Comparator<PhoneBean>() { | ||||||
|                 @Override | 				@Override | ||||||
|                 public int compare(PhoneBean o1, PhoneBean o2) { | 				public int compare(PhoneBean o1, PhoneBean o2) { | ||||||
|                     return o1.getTelPhone().compareTo(o2.getTelPhone()); | 					return o1.getTelPhone().compareTo(o2.getTelPhone()); | ||||||
|                 } | 				} | ||||||
|             }); | 			}); | ||||||
|  |  | ||||||
|         return listPhoneBean; |         return listPhoneBean; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据联系人名称查询号码(兼容拼音查询) | ||||||
|  |      * @param keyword 搜索关键词(支持汉字、拼音、拼音首字母) | ||||||
|  |      * @return 匹配的联系人列表(包含姓名和号码) | ||||||
|  |      */ | ||||||
|  |     public List<PhoneBean> getPhonesByName(String keyword) { | ||||||
|  |         List<PhoneBean> result = new ArrayList<>(); | ||||||
|  |         if (keyword == null || keyword.trim().isEmpty()) { | ||||||
|  |             return result; // 关键词为空,返回空列表 | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 获取所有联系人 | ||||||
|  |         List<PhoneBean> 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) { |     public boolean isPhoneInContacts(String szPhone) { | ||||||
|         List<PhoneBean> listPhoneDto = getPhoneList(); |         List<PhoneBean> listPhoneDto = getPhoneList(); | ||||||
|         LogUtils.d(TAG, String.format("isPhoneInContacts(...) listPhoneDto.size() %d", listPhoneDto.size())); |         LogUtils.d(TAG, String.format("isPhoneInContacts(...) listPhoneDto.size() %d", listPhoneDto.size())); | ||||||
| @@ -70,49 +185,56 @@ public class PhoneUtil { | |||||||
|         } |         } | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     boolean isTheSamePhoneNumber(String szNum1, String szNum2) { |     public String getNameByPhone(String szPhone) { | ||||||
|         //LogUtils.d(TAG, String.format("szNum1 %s\nszNum2 %s", szNum1, szNum2)); |         if (szPhone == null || szPhone.equals("")) { | ||||||
|         if(szNum1.equals(szNum2)) { |             return ""; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         List<PhoneBean> 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 ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public boolean isTheSamePhoneNumber(String szNum1, String szNum2) { | ||||||
|  |         if (szNum1.equals(szNum2)) { | ||||||
|             LogUtils.d(TAG, "szNum1.equals(szNum2)"); |             LogUtils.d(TAG, "szNum1.equals(szNum2)"); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|          |  | ||||||
|         if(UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum1)) { |         if (UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum1)) { | ||||||
|             if(szNum1.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum2))) { |             if (szNum1.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum2))) { | ||||||
|                 LogUtils.d(TAG, "szNum1.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum2))"); |                 LogUtils.d(TAG, "szNum1.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum2))"); | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |  | ||||||
|         if(UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum2)) { |         if (UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum2)) { | ||||||
|             if(szNum2.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum1))) { |             if (szNum2.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum1))) { | ||||||
|                 LogUtils.d(TAG, "szNum2.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum1))"); |                 LogUtils.d(TAG, "szNum2.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum1))"); | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |  | ||||||
|         LogUtils.d(TAG, "isTheSamePhoneNumber(...) return false;"); |         LogUtils.d(TAG, "isTheSamePhoneNumber(...) return false;"); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |  | ||||||
|     // 检验电话号码是否是数字 |     // 检验电话号码是否是数字 | ||||||
|     // |  | ||||||
|     public static boolean isPhoneByDigit(String szPhone) { |     public static boolean isPhoneByDigit(String szPhone) { | ||||||
|         if(!RegexPPiUtils.isPPiOK(szPhone)) { |         if (!RegexPPiUtils.isPPiOK(szPhone)) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         //String text = "这里是一些任意的文本内容"; |  | ||||||
|         String regex = "[+]?\\d+"; |         String regex = "[+]?\\d+"; | ||||||
|         Pattern pattern = Pattern.compile(regex); |         Pattern pattern = Pattern.compile(regex); | ||||||
|         Matcher matcher = pattern.matcher(szPhone); |         Matcher matcher = pattern.matcher(szPhone); | ||||||
|         LogUtils.d(TAG, String.format("matcher.matches() : %s", matcher.matches())); |         LogUtils.d(TAG, String.format("matcher.matches() : %s", matcher.matches())); | ||||||
|         /*if (matcher.matches()) { |  | ||||||
|          System.out.println("文本满足该正则表达式模式"); |  | ||||||
|          } else { |  | ||||||
|          System.out.println("文本不满足该正则表达式模式"); |  | ||||||
|          }*/ |  | ||||||
|         return matcher.matches(); |         return matcher.matches(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,9 +10,9 @@ import cc.winboll.studio.mymessagemanager.beans.AppConfigBean; | |||||||
| import android.content.Context; | import android.content.Context; | ||||||
|  |  | ||||||
| public class UnitAreaUtils { | public class UnitAreaUtils { | ||||||
|      |  | ||||||
|     public static final String TAG = "UnitAreaUtils"; |     public static final String TAG = "UnitAreaUtils"; | ||||||
|      |  | ||||||
|     static UnitAreaUtils _UnitAreaUtils; |     static UnitAreaUtils _UnitAreaUtils; | ||||||
|     Context mContext; |     Context mContext; | ||||||
|  |  | ||||||
| @@ -26,19 +26,25 @@ public class UnitAreaUtils { | |||||||
|         } |         } | ||||||
|         return _UnitAreaUtils; |         return _UnitAreaUtils; | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     public boolean isCurrentUnitAreaNumber(String szPhoneNumer) { |     public boolean isCurrentUnitAreaNumber(String szPhoneNumer) { | ||||||
|         String szUnitArea = getUnitArea(); |         String szUnitArea = getUnitArea(); | ||||||
|         LogUtils.d(TAG, String.format("szPhoneNumer.substring(1,3) %s", szPhoneNumer.substring(1,3))); | 		try { | ||||||
|         return szPhoneNumer.substring(1,3).equals(szUnitArea); | 			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) { |     public String genCurrentUnitAreaNumber(String szPhoneNumer) { | ||||||
|         String szUnitArea = getUnitArea(); |         String szUnitArea = getUnitArea(); | ||||||
|         LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea)); |         LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea)); | ||||||
|         return "+" + szUnitArea + szPhoneNumer; |         return "+" + szUnitArea + szPhoneNumer; | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     String getUnitArea() { |     String getUnitArea() { | ||||||
|         String szUnitArea = AppConfigUtil.getInstance(mContext).mAppConfigBean.getCountryCode(); |         String szUnitArea = AppConfigUtil.getInstance(mContext).mAppConfigBean.getCountryCode(); | ||||||
|         LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea)); |         LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea)); | ||||||
|   | |||||||
| @@ -0,0 +1,125 @@ | |||||||
|  | package cc.winboll.studio.mymessagemanager.views; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @Author ZhanGSKen&豆包大模型<zhangsken@188.com> | ||||||
|  |  * @Date 2025/08/23 00:39 | ||||||
|  |  * @Describe 多级拉动响应自定义控件 | ||||||
|  |  */ | ||||||
|  | import android.content.Context; | ||||||
|  | import android.util.AttributeSet; | ||||||
|  | import android.view.ViewTreeObserver; | ||||||
|  | import android.widget.ScrollView; | ||||||
|  |  | ||||||
|  | public class BottomPositionFixedScrollView extends ScrollView { | ||||||
|  | 	public static final String TAG = "BottomPositionFixedScrollView"; | ||||||
|  | 	// 记录底部对应的内容绝对位置(即底部位置在内容中的y坐标,该位置需始终保持在视图底部) | ||||||
|  | 	private int mBottomContentY = 0; | ||||||
|  | 	// 标记是否是首次布局(避免初始加载误触发) | ||||||
|  | 	private boolean isFirstLayout = true; | ||||||
|  |  | ||||||
|  | 	public BottomPositionFixedScrollView(Context context) { | ||||||
|  | 		super(context); | ||||||
|  | 		init(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public BottomPositionFixedScrollView(Context context, AttributeSet attrs) { | ||||||
|  | 		super(context, attrs); | ||||||
|  | 		init(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public BottomPositionFixedScrollView(Context context, AttributeSet attrs, int defStyle) { | ||||||
|  | 		super(context, attrs, defStyle); | ||||||
|  | 		init(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void init() { | ||||||
|  | 		// 监听布局变化(高度改变时触发) | ||||||
|  | 		getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onGlobalLayout() { | ||||||
|  | 					if (isFirstLayout) { | ||||||
|  | 						isFirstLayout = false; | ||||||
|  | 						return; | ||||||
|  | 					} | ||||||
|  | 					// 布局变化后,恢复底部位置 | ||||||
|  | 					restoreBottomPosition(); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 重写滚动事件,记录“底部对应的内容绝对位置” | ||||||
|  | 	 * (即当前视图底部边缘对应的内容y坐标,该坐标需始终保持在视图底部) | ||||||
|  | 	 */ | ||||||
|  | 	@Override | ||||||
|  | 	protected void onScrollChanged(int l, int t, int oldl, int oldt) { | ||||||
|  | 		super.onScrollChanged(l, t, oldl, oldt); | ||||||
|  | 		if (getChildCount() == 0) { | ||||||
|  | 			mBottomContentY = 0; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 内容总高度 | ||||||
|  | 		int contentHeight = getChildAt(0).getMeasuredHeight(); | ||||||
|  | 		// 视图可视高度(自身高度) | ||||||
|  | 		int scrollViewHeight = getMeasuredHeight(); | ||||||
|  | 		// 当前视图底部边缘对应的内容y坐标 = 顶部滚动距离(t) + 可视高度 | ||||||
|  | 		// (该坐标就是“底部内容的绝对位置”,需始终保持在视图底部) | ||||||
|  | 		mBottomContentY = t + scrollViewHeight; | ||||||
|  |  | ||||||
|  | 		// 避免超过内容总高度(比如内容不足一屏时,底部最多到内容底部) | ||||||
|  | 		if (mBottomContentY > contentHeight) { | ||||||
|  | 			mBottomContentY = contentHeight; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 恢复底部位置:让原记录的“底部内容绝对位置”仍保持在视图底部 | ||||||
|  | 	 */ | ||||||
|  | 	private void restoreBottomPosition() { | ||||||
|  | 		if (getChildCount() == 0) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 新的内容总高度 | ||||||
|  | 		int newContentHeight = getChildAt(0).getMeasuredHeight(); | ||||||
|  | 		// 新的视图可视高度 | ||||||
|  | 		int newScrollViewHeight = getMeasuredHeight(); | ||||||
|  |  | ||||||
|  | 		// 目标:让原mBottomContentY(底部内容绝对位置)仍位于视图底部 | ||||||
|  | 		// 此时需要的顶部滚动距离 = mBottomContentY - 新的可视高度 | ||||||
|  | 		int targetScrollY = mBottomContentY - newScrollViewHeight; | ||||||
|  |  | ||||||
|  | 		// 边界修正: | ||||||
|  | 		// 1. 不能小于0(避免滚动到负数位置) | ||||||
|  | 		// 2. 不能大于“最大可滚动距离”(内容高度 - 可视高度,避免超出内容范围) | ||||||
|  | 		int maxScrollY = Math.max(newContentHeight - newScrollViewHeight, 0); | ||||||
|  | 		targetScrollY = Math.max(targetScrollY, 0); | ||||||
|  | 		targetScrollY = Math.min(targetScrollY, maxScrollY); | ||||||
|  |  | ||||||
|  | 		// 滚动到目标位置,保持底部内容位置不变 | ||||||
|  | 		smoothScrollTo(0, targetScrollY); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 外部手动设置底部内容绝对位置(可选) | ||||||
|  | 	 */ | ||||||
|  | 	public void setBottomContentY(int bottomContentY) { | ||||||
|  | 		if (getChildCount() == 0) { | ||||||
|  | 			mBottomContentY = bottomContentY; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		// 限制不超过内容总高度 | ||||||
|  | 		int contentHeight = getChildAt(0).getMeasuredHeight(); | ||||||
|  | 		mBottomContentY = Math.min(bottomContentY, contentHeight); | ||||||
|  | 		restoreBottomPosition(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 获取当前底部内容绝对位置(可选) | ||||||
|  | 	 */ | ||||||
|  | 	public int getBottomContentY() { | ||||||
|  | 		return mBottomContentY; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -21,25 +21,63 @@ | |||||||
| 			android:layout_height="wrap_content" | 			android:layout_height="wrap_content" | ||||||
| 			android:id="@+id/activitycomposesmsRelativeLayout1"> | 			android:id="@+id/activitycomposesmsRelativeLayout1"> | ||||||
|  |  | ||||||
| 			<TextView | 			<LinearLayout | ||||||
|  | 				android:orientation="horizontal" | ||||||
| 				android:layout_width="wrap_content" | 				android:layout_width="wrap_content" | ||||||
| 				android:layout_height="wrap_content" | 				android:layout_height="wrap_content" | ||||||
| 				android:text="SMS TO : " | 				android:id="@+id/activitycomposesmsLinearLayout1" | ||||||
| 				android:id="@+id/activitycomposesmsTextView1" | 				android:gravity="center_vertical" | ||||||
| 				android:layout_alignParentLeft="true" |  | ||||||
| 				android:layout_marginLeft="10dp" |  | ||||||
| 				android:layout_centerVertical="true"/> |  | ||||||
|  |  | ||||||
| 			<EditText |  | ||||||
| 				android:layout_toRightOf="@id/activitycomposesmsTextView1" |  | ||||||
| 				android:layout_width="wrap_content" |  | ||||||
| 				android:inputType="phone" |  | ||||||
| 				android:layout_height="wrap_content" |  | ||||||
| 				android:ems="10" |  | ||||||
| 				android:id="@+id/activitycomposesmsEditText1" |  | ||||||
| 				android:layout_alignParentRight="true" | 				android:layout_alignParentRight="true" | ||||||
| 				android:layout_marginRight="10dp" | 				android:layout_marginRight="10dp" | ||||||
| 				android:layout_centerVertical="true"/> | 				android:layout_marginLeft="10dp" | ||||||
|  | 				android:layout_alignParentLeft="true"> | ||||||
|  |  | ||||||
|  | 				<TextView | ||||||
|  | 					android:layout_width="wrap_content" | ||||||
|  | 					android:layout_height="wrap_content" | ||||||
|  | 					android:text="(拼音搜索):"/> | ||||||
|  |  | ||||||
|  | 				<EditText | ||||||
|  | 					android:layout_width="80dp" | ||||||
|  | 					android:ems="10" | ||||||
|  | 					android:layout_height="wrap_content" | ||||||
|  | 					android:id="@+id/activitycomposesmsEditText2"/> | ||||||
|  |  | ||||||
|  | 				<TextView | ||||||
|  | 					android:layout_width="wrap_content" | ||||||
|  | 					android:layout_height="wrap_content" | ||||||
|  | 					android:layout_alignParentTop="true" | ||||||
|  | 					android:layout_toRightOf="@id/activitycomposesmsEditText2" | ||||||
|  | 					android:id="@+id/activitycomposesmsTextView2" | ||||||
|  | 					android:layout_weight="1.0"/> | ||||||
|  |  | ||||||
|  | 			</LinearLayout> | ||||||
|  |  | ||||||
|  | 			<LinearLayout | ||||||
|  | 				android:orientation="horizontal" | ||||||
|  | 				android:layout_below="@id/activitycomposesmsLinearLayout1" | ||||||
|  | 				android:layout_alignParentRight="true" | ||||||
|  | 				android:layout_marginRight="10dp" | ||||||
|  | 				android:layout_marginLeft="10dp" | ||||||
|  | 				android:layout_alignParentLeft="true" | ||||||
|  | 				android:layout_width="wrap_content" | ||||||
|  | 				android:layout_height="wrap_content" | ||||||
|  | 				android:gravity="center_vertical"> | ||||||
|  |  | ||||||
|  | 				<TextView | ||||||
|  | 					android:layout_width="wrap_content" | ||||||
|  | 					android:layout_height="wrap_content" | ||||||
|  | 					android:text="(SMS TO) :" | ||||||
|  | 					android:id="@+id/activitycomposesmsTextView1"/> | ||||||
|  |  | ||||||
|  | 				<EditText | ||||||
|  | 					android:layout_width="wrap_content" | ||||||
|  | 					android:inputType="phone" | ||||||
|  | 					android:layout_height="wrap_content" | ||||||
|  | 					android:ems="10" | ||||||
|  | 					android:id="@+id/activitycomposesmsEditText1"/> | ||||||
|  |  | ||||||
|  | 			</LinearLayout> | ||||||
|  |  | ||||||
| 		</RelativeLayout> | 		</RelativeLayout> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ | |||||||
| 			android:layout_width="match_parent" | 			android:layout_width="match_parent" | ||||||
| 			android:layout_height="60dp" | 			android:layout_height="60dp" | ||||||
| 			android:padding="10dp" | 			android:padding="10dp" | ||||||
| 			android:text="@string/text_onlyreceivecontacts" | 			android:text="@string/text_norulesreceivecontacts" | ||||||
| 			android:id="@+id/activitymainSwitchView2"/> | 			android:id="@+id/activitymainSwitchView2"/> | ||||||
|  |  | ||||||
| 		<cc.winboll.studio.mymessagemanager.views.ConfirmSwitchView | 		<cc.winboll.studio.mymessagemanager.views.ConfirmSwitchView | ||||||
|   | |||||||
| @@ -10,36 +10,40 @@ | |||||||
| 		android:layout_height="@dimen/toolbar_height" | 		android:layout_height="@dimen/toolbar_height" | ||||||
| 		android:id="@+id/activitysmsASupportToolbar1"/> | 		android:id="@+id/activitysmsASupportToolbar1"/> | ||||||
|  |  | ||||||
| 	<RelativeLayout | 	<ScrollView | ||||||
| 		android:orientation="vertical" |  | ||||||
| 		android:layout_width="match_parent" | 		android:layout_width="match_parent" | ||||||
| 		android:layout_height="match_parent" | 		android:layout_height="0dp" | ||||||
| 		android:paddingBottom="10dp"> | 		android:layout_weight="1.0" | ||||||
|  | 		android:id="@+id/activitysmsScrollView2"> | ||||||
|  |  | ||||||
| 		<ScrollView | 		<LinearLayout | ||||||
| 			android:layout_alignParentTop="true" | 			android:orientation="vertical" | ||||||
| 			android:layout_above="@+id/activitysmsinclude1" |  | ||||||
| 			android:layout_width="match_parent" | 			android:layout_width="match_parent" | ||||||
| 			android:layout_height="0dp" | 			android:layout_height="match_parent" | ||||||
| 			android:id="@+id/activitysmsinphoneScrollView1" | 			android:id="@+id/activitysmsLinearLayout1"> | ||||||
| 			android:layout_weight="1.0" |  | ||||||
| 			android:isScrollContainer="false"> |  | ||||||
|  |  | ||||||
| 			<cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView | 			<cc.winboll.studio.mymessagemanager.views.BottomPositionFixedScrollView | ||||||
|  | 				android:layout_width="match_parent" | ||||||
|  | 				android:layout_height="520dp" | ||||||
|  | 				android:isScrollContainer="false" | ||||||
|  | 				android:id="@+id/activitysmsScrollView1"> | ||||||
|  |  | ||||||
|  | 				<cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView | ||||||
|  | 					android:layout_width="match_parent" | ||||||
|  | 					android:layout_height="wrap_content" | ||||||
|  | 					android:id="@+id/activitysmsSMSListViewForScrollView1"/> | ||||||
|  |  | ||||||
|  | 			</cc.winboll.studio.mymessagemanager.views.BottomPositionFixedScrollView> | ||||||
|  |  | ||||||
|  | 			<include | ||||||
|  | 				layout="@layout/view_smssend" | ||||||
| 				android:layout_width="match_parent" | 				android:layout_width="match_parent" | ||||||
| 				android:layout_height="wrap_content" | 				android:layout_height="wrap_content" | ||||||
| 				android:id="@+id/activitysmsinphoneListView1"/> | 				android:id="@+id/activitysmsinclude1"/> | ||||||
|  | 			 | ||||||
|  | 		</LinearLayout> | ||||||
|  |  | ||||||
| 		</ScrollView> | 	</ScrollView> | ||||||
|  |  | ||||||
| 		<include |  | ||||||
| 			android:layout_alignParentBottom="true" |  | ||||||
| 			layout="@layout/view_smssend" |  | ||||||
| 			android:layout_width="match_parent" |  | ||||||
| 			android:layout_height="wrap_content" |  | ||||||
| 			android:id="@+id/activitysmsinclude1"/> |  | ||||||
|  |  | ||||||
| 	</RelativeLayout> |  | ||||||
|  |  | ||||||
| </LinearLayout> | </LinearLayout> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,6 +7,18 @@ | |||||||
| 	android:layout_height="wrap_content" | 	android:layout_height="wrap_content" | ||||||
| 	android:padding="10dp"> | 	android:padding="10dp"> | ||||||
|  |  | ||||||
|  | 	<TextView | ||||||
|  | 		android:layout_width="match_parent" | ||||||
|  | 		android:layout_height="wrap_content" | ||||||
|  | 		android:gravity="center" | ||||||
|  | 		android:textSize="20sp" | ||||||
|  | 		android:id="@+id/viewsmssendpart1TextView1"/> | ||||||
|  | 	 | ||||||
|  | 	<cc.winboll.studio.libaes.views.AOHPCTCSeekBar | ||||||
|  | 		android:layout_width="match_parent" | ||||||
|  | 		android:layout_height="wrap_content" | ||||||
|  | 		android:id="@+id/viewsmssendpart1AOHPCTCSeekBar1"/> | ||||||
|  |  | ||||||
| 	<EditText | 	<EditText | ||||||
|         android:scrollbars="vertical" |         android:scrollbars="vertical" | ||||||
|         android:maxHeight="150dp" |         android:maxHeight="150dp" | ||||||
| @@ -17,17 +29,6 @@ | |||||||
| 		android:id="@+id/viewsmssendpart1EditText1" | 		android:id="@+id/viewsmssendpart1EditText1" | ||||||
| 		android:background="@drawable/bg_frame"/> | 		android:background="@drawable/bg_frame"/> | ||||||
|  |  | ||||||
| 	<cc.winboll.studio.libaes.views.AOHPCTCSeekBar | 	 | ||||||
| 		android:layout_width="match_parent" |  | ||||||
| 		android:layout_height="wrap_content" |  | ||||||
| 		android:id="@+id/viewsmssendpart1AOHPCTCSeekBar1"/> |  | ||||||
|  |  | ||||||
| 	<TextView |  | ||||||
| 		android:layout_width="match_parent" |  | ||||||
| 		android:layout_height="wrap_content" |  | ||||||
| 		android:gravity="center" |  | ||||||
| 		android:textSize="20sp" |  | ||||||
| 		android:id="@+id/viewsmssendpart1TextView1"/> |  | ||||||
|  |  | ||||||
| </LinearLayout> | </LinearLayout> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ | |||||||
|     <string name="text_item_rule_clean">清理设置</string> |     <string name="text_item_rule_clean">清理设置</string> | ||||||
|     <string name="text_sendsms">发送短信</string> |     <string name="text_sendsms">发送短信</string> | ||||||
|     <string name="text_mainservice">短信服务管理总开关</string> |     <string name="text_mainservice">短信服务管理总开关</string> | ||||||
|     <string name="text_onlyreceivecontacts">只接收联系人短信</string> |     <string name="text_norulesreceivecontacts">无限制接收联系人短信</string> | ||||||
|     <string name="text_usingtts">使用TTS语音播报</string> |     <string name="text_usingtts">使用TTS语音播报</string> | ||||||
|     <string name="text_usingttsrule">使用TTS语音自定义规则</string> |     <string name="text_usingttsrule">使用TTS语音自定义规则</string> | ||||||
|     <string name="text_iamhere">短信管理服务已启动。</string> |     <string name="text_iamhere">短信管理服务已启动。</string> | ||||||
| @@ -41,6 +41,6 @@ | |||||||
|     <string name="text_appsettings">应用设置</string> |     <string name="text_appsettings">应用设置</string> | ||||||
|     <string name="text_ttsplaydelaytimes">TTS播放延迟时间(秒):</string> |     <string name="text_ttsplaydelaytimes">TTS播放延迟时间(秒):</string> | ||||||
|     <string name="msg_newsms">接收到新的消息。</string> |     <string name="msg_newsms">接收到新的消息。</string> | ||||||
|     <string name="msg_100sendmsg">>>>拉图标动到 100% 以发送信息。>>></string> |     <string name="msg_100sendmsg">>>>拉动到100%可发信息>>></string> | ||||||
|     <string name="msg_100applysettings">>>>拉图标动到 100% 应用设置。>>></string> |     <string name="msg_100applysettings">>>>拉动到100%可应用设置>>></string> | ||||||
| </resources> | </resources> | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ | |||||||
|     <string name="text_item_rule_clean">Clean Setting</string> |     <string name="text_item_rule_clean">Clean Setting</string> | ||||||
|     <string name="text_sendsms">Send SMS</string> |     <string name="text_sendsms">Send SMS</string> | ||||||
|     <string name="text_mainservice">Main Service</string> |     <string name="text_mainservice">Main Service</string> | ||||||
|     <string name="text_onlyreceivecontacts">Only Receive Contacts</string> |     <string name="text_norulesreceivecontacts">No rules Receive Contacts</string> | ||||||
|     <string name="text_usingtts">Using TTS</string> |     <string name="text_usingtts">Using TTS</string> | ||||||
|     <string name="text_usingttsrule">Using TTS Rule</string> |     <string name="text_usingttsrule">Using TTS Rule</string> | ||||||
|     <string name="text_iamhere">The main service is start.</string> |     <string name="text_iamhere">The main service is start.</string> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen