Compare commits
	
		
			15 Commits
		
	
	
		
			timestamp-
			...
			b505156211
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b505156211 | ||
| 
						 | 
					91b30fb576 | ||
| 
						 | 
					ab3ac72d54 | ||
| 
						 | 
					870e9a94fb | ||
| 
						 | 
					2421ecb943 | ||
| 
						 | 
					687fff7216 | ||
| 
						 | 
					3795cf8631 | ||
| 
						 | 
					b374f3117a | ||
| 
						 | 
					6d9adc124e | ||
| 
						 | 
					52f738b45b | ||
| 
						 | 
					9ece6778b7 | ||
| 
						 | 
					e13c8e7af0 | ||
| 
						 | 
					a4988b5b68 | ||
| 
						 | 
					5507126f6b | ||
| 
						 | 
					d381c29452 | 
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sat Jun 28 12:59:51 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
#Thu Jun 19 20:42:40 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,7 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
 | 
			
		||||
        appInfo.setAppGitOwner("Studio");
 | 
			
		||||
        appInfo.setAppGitAPPBranch(szBranchName);
 | 
			
		||||
        appInfo.setAppGitAPPSubProjectFolder(szBranchName);
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=3&extra=page%3D1");
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=2&fromuid=1");
 | 
			
		||||
        appInfo.setAppAPKName("AES");
 | 
			
		||||
        appInfo.setAppAPKFolderName("AES");
 | 
			
		||||
        //appInfo.setIsAddDebugTools(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.9"
 | 
			
		||||
        versionName "15.8"
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Jul 17 11:39:14 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Mon Jun 09 09:38:19 HKT 2025
 | 
			
		||||
stageCount=9
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.8
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
baseBetaVersion=15.8.9
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
https://github.com/aJIEw/PhoneCallApp.git
 | 
			
		||||
 | 
			
		||||
#### 介绍
 | 
			
		||||
这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。
 | 
			
		||||
通讯录与拨号
 | 
			
		||||
 | 
			
		||||
#### 软件架构
 | 
			
		||||
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
 | 
			
		||||
 
 | 
			
		||||
@@ -45,9 +45,9 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.8.0'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.1'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.1'
 | 
			
		||||
    
 | 
			
		||||
    // 权限请求框架:https://github.com/getActivity/XXPermissions
 | 
			
		||||
    api 'com.github.getActivity:XXPermissions:18.63'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Jul 17 09:57:24 HKT 2025
 | 
			
		||||
stageCount=12
 | 
			
		||||
#Tue May 20 13:02:18 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.11
 | 
			
		||||
publishVersion=15.3.2
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.12
 | 
			
		||||
baseBetaVersion=15.3.3
 | 
			
		||||
 
 | 
			
		||||
@@ -79,12 +79,12 @@ public class AboutActivity extends AppCompatActivity implements IWinBoLLActivity
 | 
			
		||||
        APPInfo appInfo = new APPInfo();
 | 
			
		||||
        appInfo.setAppName("Contacts");
 | 
			
		||||
        appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
 | 
			
		||||
        appInfo.setAppDescription("这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。");
 | 
			
		||||
        appInfo.setAppGitName("APPBase");
 | 
			
		||||
        appInfo.setAppDescription("通讯录与拨号");
 | 
			
		||||
        appInfo.setAppGitName("APP");
 | 
			
		||||
        appInfo.setAppGitOwner("Studio");
 | 
			
		||||
        appInfo.setAppGitAPPBranch(szBranchName);
 | 
			
		||||
        appInfo.setAppGitAPPSubProjectFolder(szBranchName);
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=4&extra=page%3D1");
 | 
			
		||||
        appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
 | 
			
		||||
        appInfo.setAppAPKName("Contacts");
 | 
			
		||||
        appInfo.setAppAPKFolderName("Contacts");
 | 
			
		||||
        return new AboutView(mContext, appInfo);
 | 
			
		||||
 
 | 
			
		||||
@@ -198,9 +198,6 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
 | 
			
		||||
            settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString()));
 | 
			
		||||
            settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString()));
 | 
			
		||||
            settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString()));
 | 
			
		||||
			
 | 
			
		||||
			// 应用效果提示
 | 
			
		||||
			ToastUtils.show((settingsModel.getDunTotalCount() == 1)?"电话骚扰防御力几乎为0。":String.format("以下设置将在连拨%d次后接通电话。", settingsModel.getDunTotalCount()));
 | 
			
		||||
        }
 | 
			
		||||
        settingsModel.setIsEnableDun(isEnableDun);
 | 
			
		||||
        Rules.getInstance(this).saveDun();
 | 
			
		||||
@@ -210,7 +207,6 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
 | 
			
		||||
        etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
 | 
			
		||||
        etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
 | 
			
		||||
        etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
 | 
			
		||||
		
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateStreamVolumeTextView() {
 | 
			
		||||
@@ -247,9 +243,6 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
 | 
			
		||||
        Rules.getInstance(this).resetDefaultBoBullToonURL();
 | 
			
		||||
        EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
 | 
			
		||||
        etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
 | 
			
		||||
		
 | 
			
		||||
		final TomCat tomCat = TomCat.getInstance(this);
 | 
			
		||||
		tomCat.cleanBoBullToon();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void onDownloadBoBullToon(View view) {
 | 
			
		||||
@@ -337,8 +330,4 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
 | 
			
		||||
    public void onAbout(View view) {
 | 
			
		||||
        App.getWinBoLLActivityManager().startWinBoLLActivity(this, AboutActivity.class);
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	public void onLogView(View view) {
 | 
			
		||||
        App.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,18 +5,13 @@ package cc.winboll.studio.contacts.adapters;
 | 
			
		||||
 * @Date 2025/02/26 13:09:32
 | 
			
		||||
 * @Describe CallLogAdapter
 | 
			
		||||
 */
 | 
			
		||||
import android.content.ClipData;
 | 
			
		||||
import android.content.ClipboardManager;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.PopupMenu;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.contacts.R;
 | 
			
		||||
@@ -52,38 +47,6 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV
 | 
			
		||||
    public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) {
 | 
			
		||||
        final CallLogModel callLog = callLogList.get(position);
 | 
			
		||||
        holder.phoneNumber.setText(callLog.getPhoneNumber() + "☎" + mContactUtils.getContactsName(callLog.getPhoneNumber()));
 | 
			
		||||
		holder.phoneNumber.setOnLongClickListener(new View.OnLongClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public boolean onLongClick(View p1) {
 | 
			
		||||
					// 弹出复制菜单
 | 
			
		||||
					PopupMenu menu = new PopupMenu(mContext, holder.phoneNumber);
 | 
			
		||||
					//加载菜单资源
 | 
			
		||||
					menu.getMenuInflater().inflate(R.menu.toolbar_calllog_phonenumber, menu.getMenu());
 | 
			
		||||
					//设置点击事件的响应
 | 
			
		||||
					menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
 | 
			
		||||
							@Override
 | 
			
		||||
							public boolean onMenuItemClick(MenuItem menuItem) {
 | 
			
		||||
								int nItemId = menuItem.getItemId();
 | 
			
		||||
								if (nItemId == R.id.item_calllog_phonenumber_copy) {
 | 
			
		||||
									// Gets a handle to the clipboard service.
 | 
			
		||||
									ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
 | 
			
		||||
									// Creates a new text clip to put on the clipboard
 | 
			
		||||
									ClipData clip = ClipData.newPlainText("simple text", callLog.getPhoneNumber());
 | 
			
		||||
									// Set the clipboard's primary clip.
 | 
			
		||||
									clipboard.setPrimaryClip(clip);
 | 
			
		||||
									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show();
 | 
			
		||||
								}
 | 
			
		||||
 | 
			
		||||
								return true;
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
					//一定要调用show()来显示弹出式菜单
 | 
			
		||||
					menu.show();
 | 
			
		||||
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		
 | 
			
		||||
        holder.callStatus.setText(callLog.getCallStatus());
 | 
			
		||||
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
			
		||||
        holder.callDate.setText(dateFormat.format(callLog.getCallDate()));
 | 
			
		||||
 
 | 
			
		||||
@@ -5,25 +5,19 @@ package cc.winboll.studio.contacts.adapters;
 | 
			
		||||
 * @Date 2025/02/26 13:35:44
 | 
			
		||||
 * @Describe ContactAdapter
 | 
			
		||||
 */
 | 
			
		||||
import android.content.ClipData;
 | 
			
		||||
import android.content.ClipboardManager;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.PopupMenu;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.contacts.R;
 | 
			
		||||
import cc.winboll.studio.contacts.beans.ContactModel;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
 | 
			
		||||
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {
 | 
			
		||||
 | 
			
		||||
@@ -32,10 +26,8 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
 | 
			
		||||
    private static final int REQUEST_CALL_PHONE = 1;
 | 
			
		||||
 | 
			
		||||
    private List<ContactModel> contactList;
 | 
			
		||||
	Context mContext;
 | 
			
		||||
 | 
			
		||||
    public ContactAdapter(Context context, List<ContactModel> contactList) {
 | 
			
		||||
		mContext = context;
 | 
			
		||||
    public ContactAdapter(List<ContactModel> contactList) {
 | 
			
		||||
        this.contactList = contactList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -49,37 +41,6 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) {
 | 
			
		||||
        final ContactModel contact = contactList.get(position);
 | 
			
		||||
		holder.llPhoneNumberMain.setOnLongClickListener(new View.OnLongClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public boolean onLongClick(View p1) {
 | 
			
		||||
					// 弹出复制菜单
 | 
			
		||||
					PopupMenu menu = new PopupMenu(mContext, holder.llPhoneNumberMain);
 | 
			
		||||
					//加载菜单资源
 | 
			
		||||
					menu.getMenuInflater().inflate(R.menu.toolbar_contact_phonenumber, menu.getMenu());
 | 
			
		||||
					//设置点击事件的响应
 | 
			
		||||
					menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
 | 
			
		||||
							@Override
 | 
			
		||||
							public boolean onMenuItemClick(MenuItem menuItem) {
 | 
			
		||||
								int nItemId = menuItem.getItemId();
 | 
			
		||||
								if (nItemId == R.id.item_contact_phonenumber_copy) {
 | 
			
		||||
									// Gets a handle to the clipboard service.
 | 
			
		||||
									ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
 | 
			
		||||
									// Creates a new text clip to put on the clipboard
 | 
			
		||||
									ClipData clip = ClipData.newPlainText("simple text", contact.getNumber());
 | 
			
		||||
									// Set the clipboard's primary clip.
 | 
			
		||||
									clipboard.setPrimaryClip(clip);
 | 
			
		||||
									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show();
 | 
			
		||||
								}
 | 
			
		||||
 | 
			
		||||
								return true;
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
					//一定要调用show()来显示弹出式菜单
 | 
			
		||||
					menu.show();
 | 
			
		||||
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
        holder.contactName.setText(contact.getName());
 | 
			
		||||
        holder.contactNumber.setText(contact.getNumber());
 | 
			
		||||
 | 
			
		||||
@@ -108,14 +69,12 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class ContactViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
		LinearLayout llPhoneNumberMain;
 | 
			
		||||
        TextView contactName;
 | 
			
		||||
        TextView contactNumber;
 | 
			
		||||
        AOHPCTCSeekBar dialAOHPCTCSeekBar;
 | 
			
		||||
        
 | 
			
		||||
        public ContactViewHolder(@NonNull View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
			llPhoneNumberMain = itemView.findViewById(R.id.itemcontactLinearLayout1);
 | 
			
		||||
            contactName = itemView.findViewById(R.id.contact_name);
 | 
			
		||||
            contactNumber = itemView.findViewById(R.id.contact_number);
 | 
			
		||||
            dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ package cc.winboll.studio.contacts.adapters;
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.MotionEvent;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
@@ -21,7 +20,6 @@ import cc.winboll.studio.contacts.R;
 | 
			
		||||
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
 | 
			
		||||
import cc.winboll.studio.contacts.dun.Rules;
 | 
			
		||||
import cc.winboll.studio.contacts.views.LeftScrollView;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@@ -62,10 +60,6 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
 | 
			
		||||
            final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder;
 | 
			
		||||
            String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText();
 | 
			
		||||
            simpleViewHolder.tvRuleText.setText(szView);
 | 
			
		||||
            simpleViewHolder.checkBoxAllow.setChecked(model.isAllowConnection());
 | 
			
		||||
			simpleViewHolder.checkBoxAllow.setEnabled(false);
 | 
			
		||||
            simpleViewHolder.checkBoxEnable.setChecked(model.isEnable());
 | 
			
		||||
			simpleViewHolder.checkBoxEnable.setEnabled(false);
 | 
			
		||||
            simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){
 | 
			
		||||
 | 
			
		||||
                    @Override
 | 
			
		||||
@@ -221,22 +215,16 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
 | 
			
		||||
 | 
			
		||||
        private final LeftScrollView scrollView;
 | 
			
		||||
        private final TextView tvRuleText;
 | 
			
		||||
		CheckBox checkBoxAllow;
 | 
			
		||||
        CheckBox checkBoxEnable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public SimpleViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
            scrollView = itemView.findViewById(R.id.scrollView);
 | 
			
		||||
			LayoutInflater inflater = LayoutInflater.from(itemView.getContext());
 | 
			
		||||
		    View viewContent = inflater.inflate(R.layout.view_phone_connect_rule_simple_content, parent, false);
 | 
			
		||||
            tvRuleText = viewContent.findViewById(R.id.ruletext_tv);
 | 
			
		||||
            checkBoxAllow = viewContent.findViewById(R.id.checkbox_allow);
 | 
			
		||||
            checkBoxEnable = viewContent.findViewById(R.id.checkbox_enable);
 | 
			
		||||
            //tvRuleText = new TextView(itemView.getContext());
 | 
			
		||||
            //tvRuleText = itemView.findViewById(R.id.ruletext_tv);
 | 
			
		||||
            tvRuleText = new TextView(itemView.getContext());
 | 
			
		||||
            scrollView.setContentWidth(parent.getWidth());
 | 
			
		||||
            //scrollView.setContentWidth(600);
 | 
			
		||||
            scrollView.addContentLayout(viewContent);
 | 
			
		||||
            scrollView.addContentLayout(tvRuleText);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@@ -255,9 +243,5 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
 | 
			
		||||
            buttonConfirm = itemView.findViewById(R.id.button_confirm);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	private void setCheckBoxTouchListener(CheckBox checkBox) {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ public class TomCat {
 | 
			
		||||
        }
 | 
			
		||||
        return _TomCat;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    public String getDefaultBobulltoonUrl() {
 | 
			
		||||
        return mContext.getString(R.string.default_bobulltoon_url);
 | 
			
		||||
    }
 | 
			
		||||
@@ -123,7 +123,7 @@ public class TomCat {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 更新新文件
 | 
			
		||||
            if (downloadAndExtractZip(zipUrl, destinationFolder)) {
 | 
			
		||||
            if(downloadAndExtractZip(zipUrl, destinationFolder)) {
 | 
			
		||||
                LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
@@ -155,19 +155,6 @@ public class TomCat {
 | 
			
		||||
        return mContext.getExternalFilesDir(TAG);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	public void cleanBoBullToon() {
 | 
			
		||||
		String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径
 | 
			
		||||
		// 删除旧文件
 | 
			
		||||
		File fOldFolder = new File(destinationFolder);
 | 
			
		||||
		if (fOldFolder.exists()) {
 | 
			
		||||
			deleteFolderRecursive(fOldFolder);
 | 
			
		||||
			fOldFolder.mkdirs();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ToastUtils.show("已清空 BoBullToon 数据!");
 | 
			
		||||
		LogUtils.d(TAG, "已清空 BoBullToon 数据");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    public boolean loadPhoneBoBullToon() {
 | 
			
		||||
        listPhoneBoBullToon.clear();
 | 
			
		||||
        File fBoBullToon = new File(getWorkingFolder(), "bobulltoon");
 | 
			
		||||
 
 | 
			
		||||
@@ -145,14 +145,6 @@ public class Rules {
 | 
			
		||||
            LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 检验拨不通号码群
 | 
			
		||||
        if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
 | 
			
		||||
            LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
 | 
			
		||||
            isDefend = true;
 | 
			
		||||
            isConnect = false;
 | 
			
		||||
            LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 查询通讯录是否有该联系人
 | 
			
		||||
        boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber);
 | 
			
		||||
        if (!isDefend) {
 | 
			
		||||
@@ -166,6 +158,14 @@ public class Rules {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 检验拨不通号码群
 | 
			
		||||
        if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
 | 
			
		||||
            LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
 | 
			
		||||
            isDefend = true;
 | 
			
		||||
            isConnect = false;
 | 
			
		||||
            LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 正则匹配规则名单校验
 | 
			
		||||
        if (!isDefend) {
 | 
			
		||||
            for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ public class ContactsFragment extends Fragment {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState);
 | 
			
		||||
        recyclerView = view.findViewById(R.id.contacts_recycler_view);
 | 
			
		||||
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
 | 
			
		||||
        contactAdapter = new ContactAdapter(getContext(), contactList);
 | 
			
		||||
        contactAdapter = new ContactAdapter(contactList);
 | 
			
		||||
        recyclerView.setAdapter(contactAdapter);
 | 
			
		||||
 | 
			
		||||
        searchEditText = view.findViewById(R.id.search_edit_text);
 | 
			
		||||
 
 | 
			
		||||
@@ -47,8 +47,8 @@ public class LeftScrollView extends HorizontalScrollView {
 | 
			
		||||
        init();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addContentLayout(View viewContent) {
 | 
			
		||||
        contentLayout.addView(viewContent, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
 | 
			
		||||
    public void addContentLayout(TextView textView) {
 | 
			
		||||
        contentLayout.addView(textView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setContentWidth(int contentWidth) {
 | 
			
		||||
 
 | 
			
		||||
@@ -269,19 +269,6 @@
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="center_horizontal">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="<<==向左拉动列表项可编辑内容"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
				android:id="@+id/recycler_view"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
@@ -300,12 +287,6 @@
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="right">
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="LogView"
 | 
			
		||||
					android:onClick="onLogView"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,8 @@
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Add Demo Rules(While size is 0) and Test"
 | 
			
		||||
			android:onClick="onTestMain"
 | 
			
		||||
			android:textSize="10sp"
 | 
			
		||||
			android:textAllCaps="false"/>
 | 
			
		||||
			android:text="Test Main"
 | 
			
		||||
			android:onClick="onTestMain"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -45,8 +43,7 @@
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Test Phone"
 | 
			
		||||
			android:onClick="onTestPhone"
 | 
			
		||||
			android:textAllCaps="false"/>
 | 
			
		||||
			android:onClick="onTestPhone"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,7 @@
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/itemcontactLinearLayout1">
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:id="@+id/contact_number"
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
			android:id="@+id/checkbox_allow"
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="连接"/>
 | 
			
		||||
			android:text="允许连接"/>
 | 
			
		||||
 | 
			
		||||
        <CheckBox
 | 
			
		||||
            android:id="@+id/checkbox_enable"
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,55 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<HorizontalScrollView
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:scrollbars="none"
 | 
			
		||||
    android:id="@+id/scrollView">
 | 
			
		||||
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:orientation="horizontal">
 | 
			
		||||
 | 
			
		||||
        <!-- 内容区域 -->
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
            android:id="@+id/content_layout"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:padding="16dp"
 | 
			
		||||
            android:background="@color/white">
 | 
			
		||||
            <!-- 这里放置你的列表项内容 -->
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                android:id="@+id/text_view"
 | 
			
		||||
                android:layout_width="0dp"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:layout_weight="1"
 | 
			
		||||
                android:textSize="16sp"/>
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
 | 
			
		||||
        <!-- 操作按钮 -->
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
            android:id="@+id/action_layout"
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="match_parent"
 | 
			
		||||
            android:orientation="horizontal"
 | 
			
		||||
            android:background="@color/lightgray">
 | 
			
		||||
 | 
			
		||||
            <Button
 | 
			
		||||
                android:id="@+id/edit_btn"
 | 
			
		||||
                android:layout_width="80dp"
 | 
			
		||||
                android:layout_height="match_parent"
 | 
			
		||||
                android:text="编辑"
 | 
			
		||||
                android:background="@color/blue" />
 | 
			
		||||
 | 
			
		||||
            <Button
 | 
			
		||||
                android:id="@+id/delete_btn"
 | 
			
		||||
                android:layout_width="80dp"
 | 
			
		||||
                android:layout_height="match_parent"
 | 
			
		||||
                android:text="删除"
 | 
			
		||||
                android:background="@color/red" />
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
    </LinearLayout>
 | 
			
		||||
</HorizontalScrollView>
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="horizontal"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="wrap_content"
 | 
			
		||||
	android:layout_gravity="center_vertical"
 | 
			
		||||
	android:gravity="center_vertical">
 | 
			
		||||
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="Text"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
		android:id="@+id/ruletext_tv"/>
 | 
			
		||||
 | 
			
		||||
	<CheckBox
 | 
			
		||||
		android:id="@+id/checkbox_allow"
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="连接"
 | 
			
		||||
		android:clickable="false"
 | 
			
		||||
		android:focusable="false"/>
 | 
			
		||||
 | 
			
		||||
	<CheckBox
 | 
			
		||||
		android:id="@+id/checkbox_enable"
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:text="启用"
 | 
			
		||||
		android:clickable="false"
 | 
			
		||||
		android:focusable="false"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
    
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_calllog_phonenumber_copy"
 | 
			
		||||
        android:title="Copy"/>
 | 
			
		||||
    
 | 
			
		||||
</menu>
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/item_contact_phonenumber_copy"
 | 
			
		||||
        android:title="Copy"/>
 | 
			
		||||
    
 | 
			
		||||
</menu>
 | 
			
		||||
@@ -2,6 +2,6 @@
 | 
			
		||||
<resources>
 | 
			
		||||
 | 
			
		||||
    <string name="app_name">Contacts</string>
 | 
			
		||||
    <string name="default_bobulltoon_url">https://gitea.winboll.cc/Studio/BoBullToon/archive/main.zip</string>
 | 
			
		||||
    <string name="default_bobulltoon_url">http://10.8.0.12:3000/Studio/BoBullToon/archive/main.zip</string>
 | 
			
		||||
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
    
 | 
			
		||||
    // 吐司类库
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sat Jun 28 12:59:30 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
#Thu Jun 19 20:42:26 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -107,7 +107,7 @@ public class AboutView extends LinearLayout {
 | 
			
		||||
        mszAppDescription = mAPPInfo.getAppDescription();
 | 
			
		||||
        mnAppIcon = mAPPInfo.getAppIcon();
 | 
			
		||||
 | 
			
		||||
        mszWinBoLLServerHost = GlobalApplication.isDebuging() ?  "https://yun-preivew.winboll.cc": "https://yun.winboll.cc";
 | 
			
		||||
        mszWinBoLLServerHost = GlobalApplication.isDebuging() ?  "https://dev.winboll.cc": "https://www.winboll.cc";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            mszAppVersionName = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Jul 17 11:39:14 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Mon Jun 09 09:38:19 HKT 2025
 | 
			
		||||
stageCount=9
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.8
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
baseBetaVersion=15.8.9
 | 
			
		||||
 
 | 
			
		||||
@@ -45,15 +45,17 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.3'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.6'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
    
 | 
			
		||||
	api 'io.github.medyo:android-about-page:2.0.0'
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
    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'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Jul 03 13:50:15 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Mon Sep 01 08:07:48 HKT 2025
 | 
			
		||||
stageCount=8
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.1
 | 
			
		||||
publishVersion=15.3.7
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.2
 | 
			
		||||
baseBetaVersion=15.3.8
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.activitys;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 联系人查询与短信发送窗口
 | 
			
		||||
 */
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.text.Editable;
 | 
			
		||||
import android.text.TextWatcher;
 | 
			
		||||
@@ -11,13 +16,17 @@ import android.widget.RelativeLayout;
 | 
			
		||||
import android.widget.SimpleAdapter;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toolbar;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.graphics.drawable.Drawable;
 | 
			
		||||
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
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.utils.PhoneUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -26,166 +35,282 @@ import java.util.Map;
 | 
			
		||||
public class ComposeSMSActivity extends BaseActivity {
 | 
			
		||||
 | 
			
		||||
    public static String TAG = "ComposeSMSActivity";
 | 
			
		||||
 | 
			
		||||
    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";
 | 
			
		||||
    static String MAP_PHONE = "PHONE";
 | 
			
		||||
 | 
			
		||||
    String mszSMSBody;
 | 
			
		||||
    String mszScheme;
 | 
			
		||||
    String mszPhoneTo;
 | 
			
		||||
    EditText metTO;
 | 
			
		||||
    EditText metSMSBody;
 | 
			
		||||
    SimpleAdapter mSimpleAdapter;
 | 
			
		||||
    List<Map<String,Object>> mAdapterData = new ArrayList<>();
 | 
			
		||||
    ListView mlvContracts;
 | 
			
		||||
    List<PhoneBean> mListPhoneBeanContracts;
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
    AOHPCTCSeekBar mAOHPCTCSeekBar;
 | 
			
		||||
    RelativeLayout mrlContracts;
 | 
			
		||||
    private String mszSMSBody;
 | 
			
		||||
    private String mszScheme;
 | 
			
		||||
    private String mszPhoneTo;
 | 
			
		||||
    private TextView mtvTOName;
 | 
			
		||||
    private EditText metTONameSearch;
 | 
			
		||||
    private EditText metTO;
 | 
			
		||||
    private EditText metSMSBody;
 | 
			
		||||
    private SimpleAdapter mSimpleAdapter;
 | 
			
		||||
    private List<Map<String, Object>> mAdapterData = new ArrayList<Map<String, Object>>();
 | 
			
		||||
    private ListView mlvContracts;
 | 
			
		||||
    private List<PhoneBean> mListPhoneBeanContracts;
 | 
			
		||||
    private Toolbar mToolbar;
 | 
			
		||||
    private AOHPCTCSeekBar mAOHPCTCSeekBar;
 | 
			
		||||
    private RelativeLayout mrlContracts;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_composesms);
 | 
			
		||||
        mszSMSBody = getIntent().getStringExtra(EXTRA_SMSBODY);
 | 
			
		||||
        mszScheme = getIntent().getData().getScheme();
 | 
			
		||||
        mszPhoneTo = getIntent().getData().getSchemeSpecificPart();
 | 
			
		||||
        if (!mszScheme.equals("smsto")) {
 | 
			
		||||
            // 其他方式未支持就退出
 | 
			
		||||
            finish();
 | 
			
		||||
 | 
			
		||||
        // 初始化Intent数据(增加空判断,避免NullPointerException)
 | 
			
		||||
        Intent intent = getIntent();
 | 
			
		||||
        if (intent != null) {
 | 
			
		||||
            mszSMSBody = intent.getStringExtra(EXTRA_SMSBODY);
 | 
			
		||||
            if (intent.getData() != null) {
 | 
			
		||||
                mszScheme = intent.getData().getScheme();
 | 
			
		||||
                mszPhoneTo = intent.getData().getSchemeSpecificPart();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 初始化视图
 | 
			
		||||
 | 
			
		||||
        // 校验启动方式,非smsto则退出
 | 
			
		||||
        if (mszScheme == null || !"smsto".equals(mszScheme)) {
 | 
			
		||||
            ToastUtils.show("不支持的启动方式");
 | 
			
		||||
            finish();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        initView();
 | 
			
		||||
        // 设置适配器
 | 
			
		||||
        initAdapter();
 | 
			
		||||
        // 设置搜索到的匹配位置
 | 
			
		||||
        setListViewPrePosition();
 | 
			
		||||
        initAdapter(null); // 初始加载所有联系人
 | 
			
		||||
        setListViewPrePositionByPhone();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 初始化视图
 | 
			
		||||
    //
 | 
			
		||||
    void initView() {
 | 
			
		||||
        //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame);
 | 
			
		||||
 | 
			
		||||
    private void initView() {
 | 
			
		||||
        // 初始化标题栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitycomposesmsASupportToolbar1);
 | 
			
		||||
        mToolbar = (Toolbar) findViewById(R.id.activitycomposesmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_composesms));
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人栏目框
 | 
			
		||||
        mrlContracts = findViewById(R.id.activitycomposesmsRelativeLayout1);
 | 
			
		||||
        //mrlContracts.setBackground(drawableFrame);
 | 
			
		||||
        // 初始化联系人姓名显示和搜索栏
 | 
			
		||||
        mtvTOName = (TextView) findViewById(R.id.activitycomposesmsTextView2);
 | 
			
		||||
        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(); // 按姓名搜索
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人输入框
 | 
			
		||||
        metTO = findViewById(R.id.activitycomposesmsEditText1);
 | 
			
		||||
        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
 | 
			
		||||
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                @Override  
 | 
			
		||||
                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();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void afterTextChanged(Editable s) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化提示框
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        // 初始化发送消息框
 | 
			
		||||
        metSMSBody = findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        //metSMSBody.setBackground(drawableFrame);
 | 
			
		||||
        metSMSBody.setText(mszSMSBody);
 | 
			
		||||
        metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        if (mszSMSBody != null) {
 | 
			
		||||
            metSMSBody.setText(mszSMSBody);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 设置搜索到的匹配位置
 | 
			
		||||
    //
 | 
			
		||||
    void setListViewPrePosition() {
 | 
			
		||||
        int nPrePosition = getContractsDataPrePosition(metTO.getText().toString());
 | 
			
		||||
        mlvContracts.setSelected(false);
 | 
			
		||||
        mlvContracts.setSelection(nPrePosition);
 | 
			
		||||
    // 核心优化:根据输入号码筛选列表(无结果则显示空列表)
 | 
			
		||||
    private void filterListByPhone(String inputPhone) {
 | 
			
		||||
        PhoneUtil phoneUtil = new PhoneUtil(this);
 | 
			
		||||
        List<PhoneBean> allContacts = phoneUtil.getPhoneList();
 | 
			
		||||
        List<PhoneBean> matchedContacts = new ArrayList<PhoneBean>();
 | 
			
		||||
 | 
			
		||||
        // 遍历所有联系人,匹配包含输入号码的联系人
 | 
			
		||||
        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(""); // 无结果时清空姓名显示
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 返回搜索到的匹配位置
 | 
			
		||||
    //
 | 
			
		||||
    int getContractsDataPrePosition(String szPhone) {
 | 
			
		||||
    // 根据姓名搜索联系人
 | 
			
		||||
    private void setListViewPrePositionByName() {
 | 
			
		||||
        String searchName = metTONameSearch.getText().toString().trim();
 | 
			
		||||
        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++) {
 | 
			
		||||
            if (mListPhoneBeanContracts.get(i).getTelPhone().compareTo(szPhone) > -1) {
 | 
			
		||||
            PhoneBean bean = mListPhoneBeanContracts.get(i);
 | 
			
		||||
            if (bean.getTelPhone().compareTo(szPhone) >= 0) {
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 初始化适配器
 | 
			
		||||
    //
 | 
			
		||||
    void initAdapter() {
 | 
			
		||||
        // 初始化联系人数据适配器
 | 
			
		||||
        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);
 | 
			
		||||
    // 获取姓名匹配的位置(兼容旧逻辑)
 | 
			
		||||
    private int getContractsDataPrePositionByName(String szName) {
 | 
			
		||||
        if (mListPhoneBeanContracts == null || mListPhoneBeanContracts.isEmpty()) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        // 绑定适配器与数据
 | 
			
		||||
        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() {
 | 
			
		||||
        for (int i = 0; i < mListPhoneBeanContracts.size(); i++) {
 | 
			
		||||
            if (mListPhoneBeanContracts.get(i).getName().startsWith(szName)) {
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 | 
			
		||||
                    metTO.setText(mAdapterData.get(position).get(MAP_PHONE).toString());
 | 
			
		||||
    // 初始化或更新列表适配器
 | 
			
		||||
    private void initAdapter(List<PhoneBean> initData) {
 | 
			
		||||
        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.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Message;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.view.inputmethod.InputMethodManager;
 | 
			
		||||
import android.widget.AbsListView;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
@@ -16,19 +21,17 @@ import android.widget.Toolbar;
 | 
			
		||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
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.utils.AddressUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.ViewUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.BottomPositionFixedScrollView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView;
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
 | 
			
		||||
public class SMSActivity extends BaseActivity {
 | 
			
		||||
    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 EXTRA_PHONE = "Phone";
 | 
			
		||||
    final static int MSG_SET_FOCUS = 0;
 | 
			
		||||
 | 
			
		||||
@@ -36,10 +39,11 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
    String mszPhoneTo;
 | 
			
		||||
    SMSArrayAdapter mSMSArrayAdapter;
 | 
			
		||||
    ScrollView mScrollView;
 | 
			
		||||
    BottomPositionFixedScrollView mScrollView1;
 | 
			
		||||
    EditText metSMSBody;
 | 
			
		||||
    SMSActivityBroadcastReceiver mSMSActivityBroadcastReceiver;
 | 
			
		||||
    Handler mSetFocusHandler;
 | 
			
		||||
    private boolean isImeVisible = false;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
@@ -47,45 +51,90 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
        setContentView(R.layout.activity_sms);
 | 
			
		||||
 | 
			
		||||
        initView();
 | 
			
		||||
		mSetFocusHandler = new MyHandler(SMSActivity.this);
 | 
			
		||||
        scrollScrollView();
 | 
			
		||||
        setupImeStatusListener();
 | 
			
		||||
 | 
			
		||||
        // 每隔一定时间设置输入框获得焦点
 | 
			
		||||
        //
 | 
			
		||||
        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滚动到底部
 | 
			
		||||
        setupScrollToBottomAfterWindowLoaded();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 设置输入框获得焦点的类
 | 
			
		||||
	//
 | 
			
		||||
	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();
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            super.handleMessage(msg);
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
    // 新增:窗口加载完成后让mScrollView1滚动到底部
 | 
			
		||||
    private void setupScrollToBottomAfterWindowLoaded() {
 | 
			
		||||
        final View rootView = findViewById(android.R.id.content);
 | 
			
		||||
        // 监听根布局绘制完成(窗口加载完成的标志)
 | 
			
		||||
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onGlobalLayout() {
 | 
			
		||||
					// 滚动到底部
 | 
			
		||||
					mScrollView1.post(new Runnable() {
 | 
			
		||||
							@Override
 | 
			
		||||
							public void run() {
 | 
			
		||||
								mScrollView1.fullScroll(ScrollView.FOCUS_DOWN);
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
 | 
			
		||||
					// 移除监听,避免重复触发
 | 
			
		||||
					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
 | 
			
		||||
						rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
 | 
			
		||||
					} 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
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
@@ -94,135 +143,130 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void initView() {
 | 
			
		||||
        // 发送端空号码退出
 | 
			
		||||
        mszPhoneTo = getIntent().getStringExtra(EXTRA_PHONE);
 | 
			
		||||
        if (mszPhoneTo == null || mszPhoneTo.trim().equals("")) {
 | 
			
		||||
            finish();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化标题栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitysmsASupportToolbar1);
 | 
			
		||||
        mToolbar = (Toolbar) findViewById(R.id.activitysmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + AddressUtils.getFormattedAddress(mszPhoneTo) + " >");
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 初始化滚动窗口
 | 
			
		||||
        mScrollView = findViewById(R.id.activitysmsinphoneScrollView1);
 | 
			
		||||
        mScrollView1 = (BottomPositionFixedScrollView) findViewById(R.id.activitysmsScrollView1);
 | 
			
		||||
 | 
			
		||||
        // 初始化发送消息框
 | 
			
		||||
        //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame);
 | 
			
		||||
        metSMSBody = findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        //metSMSBody.setBackground(drawableFrame);
 | 
			
		||||
        metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        metSMSBody.setOnClickListener(new View.OnClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onClick(View v) {
 | 
			
		||||
					setupScrollView1Height();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
        metSMSBody.setOnFocusChangeListener(new View.OnFocusChangeListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onFocusChange(View v, boolean hasFocus) {
 | 
			
		||||
					setupScrollView1Height();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化发送拉动控件
 | 
			
		||||
        final AOHPCTCSeekBar aOHPCTCSeekBar = findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        final AOHPCTCSeekBar aOHPCTCSeekBar = (AOHPCTCSeekBar) findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        aOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message));
 | 
			
		||||
        aOHPCTCSeekBar.setThumbOffset(20);
 | 
			
		||||
        aOHPCTCSeekBar.setOnOHPCListener(
 | 
			
		||||
            new AOHPCTCSeekBar.OnOHPCListener(){
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onOHPCommit() {
 | 
			
		||||
                    //Toast.makeText(getApplication(), "Send", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    sendSMS();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        aOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onOHPCommit() {
 | 
			
		||||
					sendSMS();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化提示框
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = (TextView) findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg);
 | 
			
		||||
 | 
			
		||||
        mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsinphoneListView1);
 | 
			
		||||
 | 
			
		||||
        // 准备数据
 | 
			
		||||
        mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsSMSListViewForScrollView1);
 | 
			
		||||
        mSMSArrayAdapter = new SMSArrayAdapter(SMSActivity.this, mszPhoneTo);
 | 
			
		||||
        mlvSMS.setAdapter(mSMSArrayAdapter);
 | 
			
		||||
 | 
			
		||||
        // 设置短信列表滚动到底部就取消已发送的通知消息
 | 
			
		||||
        //
 | 
			
		||||
        mlvSMS.setOnScrollListener(new AbsListView.OnScrollListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onScrollStateChanged(AbsListView view, int scrollState) {
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onScrollStateChanged(AbsListView view, int scrollState) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 | 
			
		||||
                    if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
 | 
			
		||||
                        // 滑动到了底部
 | 
			
		||||
                        mSMSArrayAdapter.cancelMessageNotification();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 | 
			
		||||
					if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
 | 
			
		||||
						mSMSArrayAdapter.cancelMessageNotification();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        mSMSActivityBroadcastReceiver = new SMSActivityBroadcastReceiver();
 | 
			
		||||
        IntentFilter intentFilter = new IntentFilter();
 | 
			
		||||
        intentFilter.addAction(ACTION_NOTIFY_SMS_CHANGED);
 | 
			
		||||
        IntentFilter intentFilter = new IntentFilter(ACTION_NOTIFY_SMS_CHANGED);
 | 
			
		||||
        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() {
 | 
			
		||||
        mSMSArrayAdapter.reLoadSMSList(SMSActivity.this, mszPhoneTo);
 | 
			
		||||
        mSMSArrayAdapter.notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 滚动消息文本框
 | 
			
		||||
    //
 | 
			
		||||
    void scrollScrollView() {
 | 
			
		||||
 | 
			
		||||
        ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
 | 
			
		||||
        ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 发送短信
 | 
			
		||||
    //
 | 
			
		||||
    void sendSMS() {
 | 
			
		||||
        // 空消息不发送
 | 
			
		||||
        String szSMSBody = metSMSBody.getText().toString();
 | 
			
		||||
        if (szSMSBody.equals("")) {
 | 
			
		||||
            Toast.makeText(getApplication(), "没有消息内容可发送。", Toast.LENGTH_SHORT).show();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 发送短信
 | 
			
		||||
        if (SMSUtil.sendMessageByInterface2(this, mszPhoneTo, szSMSBody)) {
 | 
			
		||||
            metSMSBody.setText("");
 | 
			
		||||
            new Handler().postDelayed(new Runnable(){
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void run() {
 | 
			
		||||
                        updateSMSView();
 | 
			
		||||
                        ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
                    }
 | 
			
		||||
                }, 1000);
 | 
			
		||||
            metSMSBody.clearFocus();
 | 
			
		||||
            new Handler().postDelayed(new Runnable() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void run() {
 | 
			
		||||
						updateSMSView();
 | 
			
		||||
						ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
					}
 | 
			
		||||
				}, 1000);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class SMSActivityBroadcastReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
        public SMSActivityBroadcastReceiver() {
 | 
			
		||||
            //LogUtils.d(TAG, "SMSActivityBroadcastReceiver()");
 | 
			
		||||
        }
 | 
			
		||||
        public SMSActivityBroadcastReceiver() {}
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onReceive(Context context, Intent intent) {
 | 
			
		||||
            switch (intent.getAction()) {
 | 
			
		||||
                case ACTION_NOTIFY_SMS_CHANGED :
 | 
			
		||||
                    //Toast.makeText(context, "ACTION_NOTIFY_SMS_CHANGED", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    updateSMSView();
 | 
			
		||||
                    ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
                    //LogUtils.d(TAG, "ACTION_NOTIFY_SMS_CHANGED");
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    throw new IllegalStateException("Unexpected value: " + intent.getAction());
 | 
			
		||||
            if (ACTION_NOTIFY_SMS_CHANGED.equals(intent.getAction())) {
 | 
			
		||||
                updateSMSView();
 | 
			
		||||
                ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new IllegalStateException("Unexpected value: " + intent.getAction());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2024/07/19 14:30:57
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 手机联系人工具类
 | 
			
		||||
 */
 | 
			
		||||
import android.content.ContentResolver;
 | 
			
		||||
@@ -11,6 +11,7 @@ import android.database.Cursor;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.provider.ContactsContract;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libapputils.utils.RegexPPiUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.PhoneBean;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
@@ -18,6 +19,11 @@ import java.util.Comparator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
public class PhoneUtil {
 | 
			
		||||
 | 
			
		||||
@@ -38,28 +44,137 @@ public class PhoneUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 读取所有联系人
 | 
			
		||||
    //
 | 
			
		||||
    public List<PhoneBean> getPhoneList() {
 | 
			
		||||
        List<PhoneBean> 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<PhoneBean>() {
 | 
			
		||||
                @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<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) {
 | 
			
		||||
        List<PhoneBean> listPhoneDto = getPhoneList();
 | 
			
		||||
        LogUtils.d(TAG, String.format("isPhoneInContacts(...) listPhoneDto.size() %d", listPhoneDto.size()));
 | 
			
		||||
@@ -70,49 +185,56 @@ public class PhoneUtil {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    boolean isTheSamePhoneNumber(String szNum1, String szNum2) {
 | 
			
		||||
        //LogUtils.d(TAG, String.format("szNum1 %s\nszNum2 %s", szNum1, szNum2));
 | 
			
		||||
        if(szNum1.equals(szNum2)) {
 | 
			
		||||
 | 
			
		||||
    public String getNameByPhone(String szPhone) {
 | 
			
		||||
        if (szPhone == null || szPhone.equals("")) {
 | 
			
		||||
            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)");
 | 
			
		||||
            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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2024/12/09 19:00:21
 | 
			
		||||
 * @Describe .* 前置预防针
 | 
			
		||||
 regex pointer preventive injection
 | 
			
		||||
 简称 RegexPPi
 | 
			
		||||
 */
 | 
			
		||||
import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
public class RegexPPiUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "RegexPPiUtils";
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 检验文本是否满足适合正则表达式模式计算
 | 
			
		||||
    //
 | 
			
		||||
    public static boolean isPPiOK(String text) {
 | 
			
		||||
        //String text = "这里是一些任意的文本内容";
 | 
			
		||||
        String regex = ".*";
 | 
			
		||||
        Pattern pattern = Pattern.compile(regex);
 | 
			
		||||
        Matcher matcher = pattern.matcher(text);
 | 
			
		||||
        /*if (matcher.matches()) {
 | 
			
		||||
         System.out.println("文本满足该正则表达式模式");
 | 
			
		||||
         } else {
 | 
			
		||||
         System.out.println("文本不满足该正则表达式模式");
 | 
			
		||||
         }*/
 | 
			
		||||
        return matcher.matches();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -8,6 +8,7 @@ package cc.winboll.studio.mymessagemanager.utils;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.util.JsonReader;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libapputils.utils.RegexPPiUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.SMSAcceptRuleBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.SMSAcceptRuleBean_V1;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 
 | 
			
		||||
@@ -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));
 | 
			
		||||
 
 | 
			
		||||
@@ -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:id="@+id/activitycomposesmsRelativeLayout1">
 | 
			
		||||
 | 
			
		||||
			<TextView
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:text="SMS TO : "
 | 
			
		||||
				android:id="@+id/activitycomposesmsTextView1"
 | 
			
		||||
				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:id="@+id/activitycomposesmsLinearLayout1"
 | 
			
		||||
				android:gravity="center_vertical"
 | 
			
		||||
				android:layout_alignParentRight="true"
 | 
			
		||||
				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>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="60dp"
 | 
			
		||||
			android:padding="10dp"
 | 
			
		||||
			android:text="@string/text_onlyreceivecontacts"
 | 
			
		||||
			android:text="@string/text_norulesreceivecontacts"
 | 
			
		||||
			android:id="@+id/activitymainSwitchView2"/>
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.mymessagemanager.views.ConfirmSwitchView
 | 
			
		||||
 
 | 
			
		||||
@@ -10,36 +10,40 @@
 | 
			
		||||
		android:layout_height="@dimen/toolbar_height"
 | 
			
		||||
		android:id="@+id/activitysmsASupportToolbar1"/>
 | 
			
		||||
 | 
			
		||||
	<RelativeLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
	<ScrollView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="match_parent"
 | 
			
		||||
		android:paddingBottom="10dp">
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
		android:id="@+id/activitysmsScrollView2">
 | 
			
		||||
 | 
			
		||||
		<ScrollView
 | 
			
		||||
			android:layout_alignParentTop="true"
 | 
			
		||||
			android:layout_above="@+id/activitysmsinclude1"
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			android:orientation="vertical"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="0dp"
 | 
			
		||||
			android:id="@+id/activitysmsinphoneScrollView1"
 | 
			
		||||
			android:layout_weight="1.0"
 | 
			
		||||
			android:isScrollContainer="false">
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/activitysmsLinearLayout1">
 | 
			
		||||
 | 
			
		||||
			<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_height="wrap_content"
 | 
			
		||||
				android:id="@+id/activitysmsinphoneListView1"/>
 | 
			
		||||
				android:id="@+id/activitysmsinclude1"/>
 | 
			
		||||
			
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		</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>
 | 
			
		||||
	</ScrollView>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,18 @@
 | 
			
		||||
	android:layout_height="wrap_content"
 | 
			
		||||
	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
 | 
			
		||||
        android:scrollbars="vertical"
 | 
			
		||||
        android:maxHeight="150dp"
 | 
			
		||||
@@ -17,17 +29,6 @@
 | 
			
		||||
		android:id="@+id/viewsmssendpart1EditText1"
 | 
			
		||||
		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>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@
 | 
			
		||||
    <string name="text_item_rule_clean">清理设置</string>
 | 
			
		||||
    <string name="text_sendsms">发送短信</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_usingttsrule">使用TTS语音自定义规则</string>
 | 
			
		||||
    <string name="text_iamhere">短信管理服务已启动。</string>
 | 
			
		||||
@@ -41,6 +41,6 @@
 | 
			
		||||
    <string name="text_appsettings">应用设置</string>
 | 
			
		||||
    <string name="text_ttsplaydelaytimes">TTS播放延迟时间(秒):</string>
 | 
			
		||||
    <string name="msg_newsms">接收到新的消息。</string>
 | 
			
		||||
    <string name="msg_100sendmsg">>>>拉图标动到 100% 以发送信息。>>></string>
 | 
			
		||||
    <string name="msg_100applysettings">>>>拉图标动到 100% 应用设置。>>></string>
 | 
			
		||||
    <string name="msg_100sendmsg">>>>拉动到100%可发信息>>></string>
 | 
			
		||||
    <string name="msg_100applysettings">>>>拉动到100%可应用设置>>></string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
    <string name="text_item_rule_clean">Clean Setting</string>
 | 
			
		||||
    <string name="text_sendsms">Send SMS</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_usingttsrule">Using TTS Rule</string>
 | 
			
		||||
    <string name="text_iamhere">The main service is start.</string>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Mon Jul 28 11:36:51 HKT 2025
 | 
			
		||||
stageCount=4
 | 
			
		||||
#Wed May 07 15:07:02 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.1
 | 
			
		||||
publishVersion=15.1.3
 | 
			
		||||
publishVersion=15.1.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.1.4
 | 
			
		||||
baseBetaVersion=15.1.2
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,6 @@
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="cc.winboll.studio.timestamp">
 | 
			
		||||
 | 
			
		||||
    <!-- 开机启动 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
 | 
			
		||||
 | 
			
		||||
    <!-- 运行前台服务 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 | 
			
		||||
 | 
			
		||||
@@ -48,20 +45,6 @@
 | 
			
		||||
 | 
			
		||||
        <service android:name=".AssistantService"/>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".receivers.MainReceiver"
 | 
			
		||||
            android:enabled="true"
 | 
			
		||||
            android:exported="false"
 | 
			
		||||
            android:directBootAware="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import android.view.View;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.Switch;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
@@ -13,8 +12,6 @@ import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.timestamp.MainService;
 | 
			
		||||
import cc.winboll.studio.timestamp.R;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.ClipboardUtil;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.TimeStampUtil;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
@@ -100,22 +97,4 @@ public class MainActivity extends AppCompatActivity {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	public void onTimeStamp(View view) {
 | 
			
		||||
		// 在这里编写按钮点击后要执行的代码
 | 
			
		||||
		TimeStampUtil.getInstance(this).genTimeStamp();
 | 
			
		||||
		String formattedDateTime = TimeStampUtil.getInstance(this).getTimeStampCopyString();
 | 
			
		||||
		ClipboardUtil.copyTextToClipboard(this, formattedDateTime);
 | 
			
		||||
		
 | 
			
		||||
		// 更新时间戳文本框显示内容
 | 
			
		||||
		EditText etTimeStamp = findViewById(R.id.et_timestamp);
 | 
			
		||||
		etTimeStamp.setText(formattedDateTime);
 | 
			
		||||
 | 
			
		||||
		// 输出一个提示音
 | 
			
		||||
		TimeStampUtil.getInstance(this).playNotifyMusic(this);
 | 
			
		||||
		
 | 
			
		||||
		// 比如显示一个 Toast
 | 
			
		||||
		Toast.makeText(this, "时间戳:\n" + TimeStampUtil.getInstance(this).getTimeStampCopyString() + "\n已拷贝到剪贴板。", Toast.LENGTH_SHORT).show();
 | 
			
		||||
		MainService.updateCopiedTimeStamp();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
package cc.winboll.studio.timestamp.receivers;
 | 
			
		||||
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.timestamp.MainService;
 | 
			
		||||
import cc.winboll.studio.timestamp.models.AppConfigsModel;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/07/28 11:15
 | 
			
		||||
 * @Describe MainReceiver
 | 
			
		||||
 */
 | 
			
		||||
public class MainReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "MainReceiver";
 | 
			
		||||
 | 
			
		||||
    static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
 | 
			
		||||
 | 
			
		||||
    AppConfigsUtil mAppConfigsUtil;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onReceive(Context context, Intent intent) {
 | 
			
		||||
        String szAction = intent.getAction();
 | 
			
		||||
        if (szAction.equals(ACTION_BOOT_COMPLETED)) {
 | 
			
		||||
            AppConfigsModel appConfigs = AppConfigsUtil.getInstance(context).loadAppConfigs();
 | 
			
		||||
			boolean isEnable = appConfigs.isEnableService();
 | 
			
		||||
			AppConfigsUtil.getInstance(context).saveAppConfigs();
 | 
			
		||||
 | 
			
		||||
			Intent intentMainService = new Intent(context, MainService.class);
 | 
			
		||||
			if (isEnable) {
 | 
			
		||||
				context.startService(intentMainService);
 | 
			
		||||
			}
 | 
			
		||||
			LogUtils.i(TAG, "System Boot And Start ManagerService Completed!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -130,10 +130,16 @@ public class NotificationHelper {
 | 
			
		||||
        mForegroundNotification.bigContentView = remoteViews;
 | 
			
		||||
 | 
			
		||||
        service.startForeground(ID_MSG_SERVICE, mForegroundNotification);
 | 
			
		||||
		
 | 
			
		||||
		TimeStampUtil.getInstance(service).playNotifyMusic(service);
 | 
			
		||||
 | 
			
		||||
        // 播放默认短信铃声
 | 
			
		||||
        Uri defaultSmsRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
 | 
			
		||||
        AudioPlayerUriUtil.playAudio(service, defaultSmsRingtoneUri);
 | 
			
		||||
 | 
			
		||||
        // 播放应用铃声
 | 
			
		||||
        // 获取MP3文件的Uri
 | 
			
		||||
        Uri soundUri = Uri.parse("android.resource://" + service.getPackageName() + "/" + R.raw.diweiyi);
 | 
			
		||||
        AudioPlayerUriUtil.playAudio(service, soundUri);
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,6 @@ package cc.winboll.studio.timestamp.utils;
 | 
			
		||||
 * @Describe TimeStampUtil
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import cc.winboll.studio.timestamp.R;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.ZoneId;
 | 
			
		||||
@@ -57,19 +55,4 @@ public class TimeStampUtil {
 | 
			
		||||
        String formattedDateTime = ldt.format(formatter);
 | 
			
		||||
        return formattedDateTime;
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	//
 | 
			
		||||
	// 播放时间戳确定时的提示音乐
 | 
			
		||||
	//
 | 
			
		||||
	public static void playNotifyMusic(Context context) {
 | 
			
		||||
        // 播放默认短信铃声
 | 
			
		||||
        //Uri defaultSmsRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
 | 
			
		||||
        //AudioPlayerUriUtil.playAudio(context, defaultSmsRingtoneUri);
 | 
			
		||||
 | 
			
		||||
        // 播放应用铃声
 | 
			
		||||
        // 获取MP3文件的Uri
 | 
			
		||||
        Uri soundUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.diweiyi);
 | 
			
		||||
        AudioPlayerUriUtil.playAudio(context, soundUri);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,180 +1,144 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="wrap_content"
 | 
			
		||||
	android:layout_height="wrap_content">
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent"
 | 
			
		||||
	android:orientation="vertical">
 | 
			
		||||
 | 
			
		||||
	<ScrollView
 | 
			
		||||
	<com.google.android.material.appbar.AppBarLayout
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
 | 
			
		||||
 | 
			
		||||
		<androidx.appcompat.widget.Toolbar
 | 
			
		||||
			android:id="@+id/toolbar"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="?attr/actionBarSize"
 | 
			
		||||
			app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
 | 
			
		||||
 | 
			
		||||
	</com.google.android.material.appbar.AppBarLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
		<Switch
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="启用时间戳常驻通知栏"
 | 
			
		||||
			android:id="@+id/activitymainSwitch1"
 | 
			
		||||
			android:onClick="onSetMainServiceStatus"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Show Format Preview:"
 | 
			
		||||
			android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Text"
 | 
			
		||||
			android:id="@+id/tv_timestampformatstring"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
		<EditText
 | 
			
		||||
			android:layout_width="0dp"
 | 
			
		||||
			android:ems="10"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/et_timestampformatstring"
 | 
			
		||||
			android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="ApplyShow"
 | 
			
		||||
			android:id="@+id/btn_saveformatstring"
 | 
			
		||||
			android:textAllCaps="false"
 | 
			
		||||
			android:onClick="onSaveFormatString"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Copy Format Preview:"
 | 
			
		||||
			android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Text"
 | 
			
		||||
			android:id="@+id/tv_timestampcopyformatstring"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
		<EditText
 | 
			
		||||
			android:layout_width="0dp"
 | 
			
		||||
			android:ems="10"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/et_timestampcopyformatstring"
 | 
			
		||||
			android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="ApplyCopy"
 | 
			
		||||
			android:id="@+id/btn_savecopyformatstring"
 | 
			
		||||
			android:textAllCaps="false"
 | 
			
		||||
			android:onClick="onSaveCopyFormatString"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
		android:gravity="center_vertical|center_horizontal">
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
		<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:orientation="vertical">
 | 
			
		||||
			android:id="@+id/logview"/>
 | 
			
		||||
 | 
			
		||||
			<com.google.android.material.appbar.AppBarLayout
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
 | 
			
		||||
 | 
			
		||||
				<androidx.appcompat.widget.Toolbar
 | 
			
		||||
					android:id="@+id/toolbar"
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="?attr/actionBarSize"
 | 
			
		||||
					app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
 | 
			
		||||
 | 
			
		||||
			</com.google.android.material.appbar.AppBarLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
				<Switch
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="启用时间戳常驻通知栏"
 | 
			
		||||
					android:id="@+id/activitymainSwitch1"
 | 
			
		||||
					android:onClick="onSetMainServiceStatus"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Show Format Preview:"
 | 
			
		||||
					android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Text"
 | 
			
		||||
					android:id="@+id/tv_timestampformatstring"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="0dp"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/et_timestampformatstring"
 | 
			
		||||
					android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="ApplyShow"
 | 
			
		||||
					android:id="@+id/btn_saveformatstring"
 | 
			
		||||
					android:textAllCaps="false"
 | 
			
		||||
					android:onClick="onSaveFormatString"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Copy Format Preview:"
 | 
			
		||||
					android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Text"
 | 
			
		||||
					android:id="@+id/tv_timestampcopyformatstring"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="0dp"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/et_timestampcopyformatstring"
 | 
			
		||||
					android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="ApplyCopy"
 | 
			
		||||
					android:id="@+id/btn_savecopyformatstring"
 | 
			
		||||
					android:textAllCaps="false"
 | 
			
		||||
					android:onClick="onSaveCopyFormatString"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="当前截取的时间戳为:"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="center_vertical|center_horizontal">
 | 
			
		||||
 | 
			
		||||
				<ImageButton
 | 
			
		||||
					android:layout_width="100dp"
 | 
			
		||||
					android:layout_height="100dp"
 | 
			
		||||
					android:onClick="onTimeStamp"
 | 
			
		||||
					android:background="@drawable/ic_timestamp"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/et_timestamp"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	</ScrollView>
 | 
			
		||||
 | 
			
		||||
	<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="200dp"
 | 
			
		||||
		android:id="@+id/logview"/>
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user