Compare commits
	
		
			74 Commits
		
	
	
		
			numtable-v
			...
			50d4cd830b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					50d4cd830b | ||
| 
						 | 
					2079822c00 | ||
| 
						 | 
					297c76f328 | ||
| 
						 | 
					43b18ee662 | ||
| 
						 | 
					d581cd9842 | ||
| 
						 | 
					cef50d087d | ||
| 
						 | 
					04df902b6b | ||
| 
						 | 
					33c71ea868 | ||
| 
						 | 
					ba861d910e | ||
| 
						 | 
					f5d9aafe43 | ||
| 
						 | 
					9e9402f84e | ||
| 
						 | 
					ec18330022 | ||
| 
						 | 
					23920a7ff1 | ||
| 
						 | 
					17c373c490 | ||
| 
						 | 
					5f7c94b349 | ||
| 
						 | 
					c2b739d345 | ||
| 
						 | 
					67a05cd457 | ||
| 
						 | 
					554ab758bf | ||
| 
						 | 
					20e118cd34 | ||
| 
						 | 
					f370ae8ffb | ||
| 
						 | 
					c92c874ea1 | ||
| 
						 | 
					90a6116c0a | ||
| 
						 | 
					45208ecbb1 | ||
| 
						 | 
					c28d655fe3 | ||
| 
						 | 
					4b5905f74e | ||
| 
						 | 
					6bd01780ec | ||
| 
						 | 
					a6699262f8 | ||
| 
						 | 
					ea2d38defc | ||
| 
						 | 
					e430b7abe4 | ||
| 
						 | 
					945eadb617 | ||
| 
						 | 
					c5bffc5eef | ||
| 
						 | 
					88597fe407 | ||
| 
						 | 
					53f985533a | ||
| 
						 | 
					a3950f13ad | ||
| 
						 | 
					c878e9dc02 | ||
| 
						 | 
					f2f7cab330 | ||
| 
						 | 
					6c8b0dcfa5 | ||
| 
						 | 
					7de8a4f084 | ||
| 
						 | 
					219c6614be | ||
| 
						 | 
					0f5bb020b9 | ||
| 
						 | 
					7794ff80ec | ||
| 
						 | 
					7463ad3352 | ||
| 
						 | 
					753032efed | ||
| 
						 | 
					2b4c43c9af | ||
| 
						 | 
					711c98d556 | ||
| 
						 | 
					202205588a | ||
| 
						 | 
					42c4978b44 | ||
| 
						 | 
					1a2b7b862d | ||
| 
						 | 
					eb253b374f | ||
| 
						 | 
					c4e88e9593 | ||
| 
						 | 
					08d9d92ae4 | ||
| 
						 | 
					74841c08dc | ||
| 
						 | 
					945bacb825 | ||
| 
						 | 
					0e464495fd | ||
| 
						 | 
					e8682ce410 | ||
| 
						 | 
					2e4003dae0 | ||
| 
						 | 
					198b0975ce | ||
| 
						 | 
					24a578a9d2 | ||
| 
						 | 
					46de24447f | ||
| 
						 | 
					ac1c008035 | ||
| 
						 | 
					b124487cb1 | ||
| 
						 | 
					9621d35f79 | ||
| 
						 | 
					17de0832a6 | ||
| 
						 | 
					1320984829 | ||
| 
						 | 
					abf1e5ba42 | ||
| 
						 | 
					1cd2f88038 | ||
| 
						 | 
					3f6e583d68 | ||
| 
						 | 
					271456bfcd | ||
| 
						 | 
					ee5458d82c | ||
| 
						 | 
					3a83367f71 | ||
| 
						 | 
					74b9350a6a | ||
| 
						 | 
					d2858f23f7 | ||
| 
						 | 
					89dac91cc6 | ||
| 
						 | 
					3809c1bcab | 
@@ -113,10 +113,10 @@ if [[ $? -eq 0 ]]; then
 | 
			
		||||
    # 如果Git已经提交了所有代码就执行标签和应用发布操作
 | 
			
		||||
 | 
			
		||||
    # 预先询问是否添加工作流标签
 | 
			
		||||
    echo "Add Github Workflows Tag? (yes/No)"
 | 
			
		||||
	result=$(askAddWorkflowsTag)
 | 
			
		||||
	nAskAddWorkflowsTag=$?
 | 
			
		||||
	echo $result
 | 
			
		||||
    #echo "Add Github Workflows Tag? (yes/No)"
 | 
			
		||||
	#result=$(askAddWorkflowsTag)
 | 
			
		||||
	#nAskAddWorkflowsTag=$?
 | 
			
		||||
	#echo $result
 | 
			
		||||
 | 
			
		||||
    # 发布应用
 | 
			
		||||
	echo "Publishing WinBoLL APK ..."
 | 
			
		||||
@@ -138,17 +138,17 @@ if [[ $? -eq 0 ]]; then
 | 
			
		||||
	fi
 | 
			
		||||
    
 | 
			
		||||
    # 添加 GitHub 工作流标签
 | 
			
		||||
	if [[ $nAskAddWorkflowsTag -eq 1 ]]; then
 | 
			
		||||
	#if [[ $nAskAddWorkflowsTag -eq 1 ]]; then
 | 
			
		||||
	    # 如果用户选择添加工作流标签
 | 
			
		||||
    	result=$(addWorkflowsTag $1)
 | 
			
		||||
		if [[ $? -eq 0 ]]; then
 | 
			
		||||
		    echo $result
 | 
			
		||||
    	#result=$(addWorkflowsTag $1)
 | 
			
		||||
		#if [[ $? -eq 0 ]]; then
 | 
			
		||||
		#    echo $result
 | 
			
		||||
		    # 工作流标签添加成功
 | 
			
		||||
		else
 | 
			
		||||
			echo -e "${0}: addWorkflowsTag $1\n${result}\nAdd workflows tag cancel."
 | 
			
		||||
			exit 1 # addWorkflowsTag 异常
 | 
			
		||||
		fi
 | 
			
		||||
	fi
 | 
			
		||||
		#else
 | 
			
		||||
			#echo -e "${0}: addWorkflowsTag $1\n${result}\nAdd workflows tag cancel."
 | 
			
		||||
			#exit 1 # addWorkflowsTag 异常
 | 
			
		||||
		#fi
 | 
			
		||||
	#fi
 | 
			
		||||
	
 | 
			
		||||
	## 清理更新描述文件内容
 | 
			
		||||
	echo "" > $1/app_update_description.txt
 | 
			
		||||
 
 | 
			
		||||
@@ -114,9 +114,11 @@
 | 
			
		||||
 | 
			
		||||
# 本项目要实际运用需要注意以下几个步骤:
 | 
			
		||||
# 在项目根目录下:
 | 
			
		||||
## 1. 项目模块编译环境设置(必须),settings.gradle-demo 要复制为 settings.gradle,并取消相应项目模块的注释。
 | 
			
		||||
## 2. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。
 | 
			
		||||
## 3. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息。
 | 
			
		||||
## ★. 项目模块编译环境设置(必须),settings.gradle-demo 要复制为 settings.gradle,并取消相应项目模块的注释。
 | 
			
		||||
## ★. 项目 Android SDK 编译环境设置(可选),local.properties-demo 要复制为 local.properties,并按需要设置 Android SDK 目录。
 | 
			
		||||
## ★. 应用签名密钥 keystore 设置问题。一般调试编译只需用【Termux】cd 进 GenKeyStore 目录执行 $ bash gen_debug_keystore.sh 命令即可完成设置。
 | 
			
		||||
## ☆. 应用 WiBoLL 签名密钥配置问题<非必须考虑>。设置时需要 clone 【keystore】模块源码并拷贝模块目录的 appkey.jks 与 appkey.keystore 到项目根目录即可。
 | 
			
		||||
## ☆. 类库型模块编译环境设置(可选),winboll.properties-demo 要复制为 winboll.properties,并按需要设置 WinBoLL Maven 库登录用户信息。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ☆类库型项目编译方法
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // 项目模块目录的 build.gradle 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.8" 
 | 
			
		||||
        versionName "15.9" 
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sun Jun 08 21:14:52 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Sat Jun 28 12:59:51 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.1
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.2
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
 
 | 
			
		||||
@@ -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=2&fromuid=1");
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=3&extra=page%3D1");
 | 
			
		||||
        appInfo.setAppAPKName("AES");
 | 
			
		||||
        appInfo.setAppAPKFolderName("AES");
 | 
			
		||||
        //appInfo.setIsAddDebugTools(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sun Jun 01 08:03:56 GMT 2025
 | 
			
		||||
#Thu Jun 19 12:49:47 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.0
 | 
			
		||||
publishVersion=15.0.0
 | 
			
		||||
buildCount=24
 | 
			
		||||
buildCount=26
 | 
			
		||||
baseBetaVersion=15.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.8"
 | 
			
		||||
        versionName "15.9"
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Jun 05 08:59:45 GMT 2025
 | 
			
		||||
stageCount=8
 | 
			
		||||
#Thu Jul 17 11:39:14 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.7
 | 
			
		||||
buildCount=14
 | 
			
		||||
baseBetaVersion=15.8.8
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sun May 04 05:32:00 GMT 2025
 | 
			
		||||
stageCount=1
 | 
			
		||||
#Tue Jun 24 09:54:47 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.2
 | 
			
		||||
publishVersion=15.2.0
 | 
			
		||||
buildCount=74
 | 
			
		||||
baseBetaVersion=15.2.1
 | 
			
		||||
publishVersion=15.2.2
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.2.3
 | 
			
		||||
 
 | 
			
		||||
@@ -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.8.0'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.1'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.1'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.3'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.5'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
    
 | 
			
		||||
    // 权限请求框架:https://github.com/getActivity/XXPermissions
 | 
			
		||||
    api 'com.github.getActivity:XXPermissions:18.63'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue May 20 13:02:18 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
#Sun Aug 31 06:05:42 CST 2025
 | 
			
		||||
stageCount=17
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.2
 | 
			
		||||
publishVersion=15.3.16
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.3
 | 
			
		||||
baseBetaVersion=15.3.17
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
package cc.winboll.studio.contacts;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 主窗口
 | 
			
		||||
 */
 | 
			
		||||
import android.Manifest;
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.ActivityManager;
 | 
			
		||||
@@ -8,6 +13,7 @@ import android.content.Intent;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.telecom.TelecomManager;
 | 
			
		||||
import android.telephony.PhoneStateListener;
 | 
			
		||||
import android.telephony.TelephonyManager;
 | 
			
		||||
@@ -41,7 +47,7 @@ import java.util.List;
 | 
			
		||||
 | 
			
		||||
final public class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
 | 
			
		||||
 | 
			
		||||
	public static final String TAG = "MainActivity";
 | 
			
		||||
    public static final String TAG = "MainActivity";
 | 
			
		||||
 | 
			
		||||
    public static final int REQUEST_HOME_ACTIVITY = 0;
 | 
			
		||||
    public static final int REQUEST_ABOUT_ACTIVITY = 1;
 | 
			
		||||
@@ -55,13 +61,10 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
 | 
			
		||||
    MainServiceBean mMainServiceBean;
 | 
			
		||||
    private TabLayout tabLayout;
 | 
			
		||||
    private ViewPager viewPager;
 | 
			
		||||
    private List<View> views; //用来存放放进ViewPager里面的布局
 | 
			
		||||
    //实例化存储imageView(导航原点)的集合
 | 
			
		||||
    private List<View> views; 
 | 
			
		||||
    ImageView[] imageViews;
 | 
			
		||||
    //MyPagerAdapter adapter;//适配器
 | 
			
		||||
    MyPagerAdapter pagerAdapter;
 | 
			
		||||
    LinearLayout linearLayout;//下标所在在LinearLayout布局里
 | 
			
		||||
    int currentPoint = 0;//当前被选中中页面的下标
 | 
			
		||||
    LinearLayout linearLayout;
 | 
			
		||||
    int currentPoint = 0;
 | 
			
		||||
 | 
			
		||||
    private TelephonyManager telephonyManager;
 | 
			
		||||
    private MyPhoneStateListener phoneStateListener;
 | 
			
		||||
@@ -70,30 +73,6 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
 | 
			
		||||
 | 
			
		||||
    private static final int DIALER_REQUEST_CODE = 1;
 | 
			
		||||
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public Activity getActivity() {
 | 
			
		||||
//        return this;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public APPInfo getAppInfo() {
 | 
			
		||||
//        String szBranchName = "contacts";
 | 
			
		||||
//
 | 
			
		||||
//        APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
 | 
			
		||||
//        appInfo.setAppName("Contacts");
 | 
			
		||||
//        appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
 | 
			
		||||
//        appInfo.setAppDescription("Contacts Description");
 | 
			
		||||
//        appInfo.setAppGitName("APP");
 | 
			
		||||
//        appInfo.setAppGitOwner("Studio");
 | 
			
		||||
//        appInfo.setAppGitAPPBranch(szBranchName);
 | 
			
		||||
//        appInfo.setAppGitAPPSubProjectFolder(szBranchName);
 | 
			
		||||
//        appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
 | 
			
		||||
//        appInfo.setAppAPKName("Contacts");
 | 
			
		||||
//        appInfo.setAppAPKFolderName("Contacts");
 | 
			
		||||
//        return appInfo;
 | 
			
		||||
//        return null;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
@@ -107,89 +86,62 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        // 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
 | 
			
		||||
        //if (prosessIntents(getIntent())) return;
 | 
			
		||||
        // 以下正常创建主窗口
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        _MainActivity = this;
 | 
			
		||||
        setContentView(R.layout.activity_main);
 | 
			
		||||
 | 
			
		||||
        // 初始化工具栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitymainToolbar1);
 | 
			
		||||
        // 初始化工具栏(仅加载基础UI)
 | 
			
		||||
        mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1);
 | 
			
		||||
        setSupportActionBar(mToolbar);
 | 
			
		||||
//        if (isEnableDisplayHomeAsUp()) {
 | 
			
		||||
//            // 显示后退按钮
 | 
			
		||||
//            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
//        }
 | 
			
		||||
        getSupportActionBar().setSubtitle(TAG);
 | 
			
		||||
 | 
			
		||||
        tabLayout = findViewById(R.id.tabLayout);
 | 
			
		||||
        viewPager = findViewById(R.id.viewPager);
 | 
			
		||||
        tabLayout = (TabLayout) findViewById(R.id.tabLayout);
 | 
			
		||||
        viewPager = (ViewPager) findViewById(R.id.viewPager);
 | 
			
		||||
 | 
			
		||||
        // 创建Fragment列表和标题列表
 | 
			
		||||
        fragmentList = new ArrayList<>();
 | 
			
		||||
        tabTitleList = new ArrayList<>();
 | 
			
		||||
        // 创建Fragment列表(仅实例化,不加载数据)
 | 
			
		||||
        fragmentList = new ArrayList<Fragment>();
 | 
			
		||||
        tabTitleList = new ArrayList<String>();
 | 
			
		||||
        fragmentList.add(CallLogFragment.newInstance(0));
 | 
			
		||||
        fragmentList.add(ContactsFragment.newInstance(1));
 | 
			
		||||
        fragmentList.add(ContactsFragment.newInstance(1)); // 延迟加载联系人数据
 | 
			
		||||
        fragmentList.add(LogFragment.newInstance(2));
 | 
			
		||||
        tabTitleList.add("通话记录");
 | 
			
		||||
        tabTitleList.add("联系人");
 | 
			
		||||
        tabTitleList.add("应用日志");
 | 
			
		||||
 | 
			
		||||
        // 设置ViewPager的适配器
 | 
			
		||||
        // 设置ViewPager适配器
 | 
			
		||||
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
 | 
			
		||||
        viewPager.setAdapter(adapter);
 | 
			
		||||
 | 
			
		||||
        // 关键:关闭预加载,仅当前页初始化
 | 
			
		||||
        viewPager.setOffscreenPageLimit(0);
 | 
			
		||||
 | 
			
		||||
        // 关联TabLayout和ViewPager
 | 
			
		||||
        tabLayout.setupWithViewPager(viewPager);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//        initData();
 | 
			
		||||
//        initView();
 | 
			
		||||
//        //initPoint();//调用初始化导航原点的方法
 | 
			
		||||
//        viewPager.addOnPageChangeListener(this);//滑动事件
 | 
			
		||||
 | 
			
		||||
        //ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
 | 
			
		||||
        //MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
 | 
			
		||||
        //viewPager.setAdapter(pagerAdapter);
 | 
			
		||||
        //TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1);
 | 
			
		||||
        //tabLayout.setupWithViewPager(viewPager);
 | 
			
		||||
 | 
			
		||||
//        mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
			
		||||
//        if (mMainServiceBean == null) {
 | 
			
		||||
//            mMainServiceBean = new MainServiceBean();
 | 
			
		||||
//        }
 | 
			
		||||
//        cbMainService = findViewById(R.id.activitymainCheckBox1);
 | 
			
		||||
//        cbMainService.setChecked(mMainServiceBean.isEnable());
 | 
			
		||||
//        cbMainService.setOnClickListener(new View.OnClickListener(){
 | 
			
		||||
//                @Override
 | 
			
		||||
//                public void onClick(View view) {
 | 
			
		||||
//                    if (cbMainService.isChecked()) {
 | 
			
		||||
//                        MainService.startMainService(MainActivity.this);
 | 
			
		||||
//                    } else {
 | 
			
		||||
//                        MainService.stopMainService(MainActivity.this);
 | 
			
		||||
//                    }
 | 
			
		||||
//                }
 | 
			
		||||
//            });
 | 
			
		||||
 | 
			
		||||
        // 初始化服务状态(延迟启动非核心服务)
 | 
			
		||||
        MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
			
		||||
        if (mMainServiceBean == null) {
 | 
			
		||||
            mMainServiceBean = new MainServiceBean();
 | 
			
		||||
            MainServiceBean.saveBean(this, mMainServiceBean);
 | 
			
		||||
        }
 | 
			
		||||
        if (mMainServiceBean.isEnable()) {
 | 
			
		||||
            MainService.startMainService(this);
 | 
			
		||||
            // 延迟1秒启动服务,避免阻塞启动
 | 
			
		||||
            new Handler().postDelayed(new Runnable() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void run() {
 | 
			
		||||
						MainService.startMainService(MainActivity.this);
 | 
			
		||||
					}
 | 
			
		||||
				}, 1000);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化TelephonyManager和PhoneStateListener
 | 
			
		||||
        // 初始化电话状态监听(基础功能保留)
 | 
			
		||||
        telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
 | 
			
		||||
        phoneStateListener = new MyPhoneStateListener();
 | 
			
		||||
        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // ViewPager的适配器
 | 
			
		||||
    // ViewPager适配器(Java 7语法)
 | 
			
		||||
    private class MyPagerAdapter extends FragmentPagerAdapter {
 | 
			
		||||
 | 
			
		||||
        private List<Fragment> fragmentList;
 | 
			
		||||
@@ -226,91 +178,22 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
 | 
			
		||||
        _MainActivity.startActivity(intent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //初始化view,即显示的图片
 | 
			
		||||
//    void initView() {
 | 
			
		||||
//        viewPager = findViewById(R.id.activitymainViewPager1);
 | 
			
		||||
//        pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
 | 
			
		||||
//        viewPager.setAdapter(pagerAdapter);
 | 
			
		||||
//        //adapter = new MyPagerAdapter(views);
 | 
			
		||||
//        //viewPager = findViewById(R.id.activitymainViewPager1);
 | 
			
		||||
//        //viewPager.setAdapter(adapter);
 | 
			
		||||
//        //linearLayout = findViewById(R.id.activitymainLinearLayout1);
 | 
			
		||||
//        //initPoint();//初始化页面下方的点
 | 
			
		||||
//        viewPager.setOnPageChangeListener(this);
 | 
			
		||||
//
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    //初始化所要显示的布局
 | 
			
		||||
//    void initData() {
 | 
			
		||||
//        LayoutInflater inflater = LayoutInflater.from(getActivity());
 | 
			
		||||
//        View view1 = inflater.inflate(R.layout.fragment_call_log, viewPager, false);
 | 
			
		||||
//        View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
 | 
			
		||||
//        View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
 | 
			
		||||
//
 | 
			
		||||
//        views = new ArrayList<>();
 | 
			
		||||
//        views.add(view1);
 | 
			
		||||
//        views.add(view2);
 | 
			
		||||
//        views.add(view3);
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
//    void initPoint() {
 | 
			
		||||
//        imageViews = new ImageView[5];//实例化5个图片
 | 
			
		||||
//        for (int i = 0; i < linearLayout.getChildCount(); i++) {
 | 
			
		||||
//            imageViews[i] = (ImageView) linearLayout.getChildAt(i);
 | 
			
		||||
//            imageViews[i].setImageResource(R.drawable.ic_launcher);
 | 
			
		||||
//            imageViews[i].setOnClickListener(this);//点击导航点,即可跳转
 | 
			
		||||
//            imageViews[i].setTag(i);//重复利用实例化的对象
 | 
			
		||||
//        }
 | 
			
		||||
//        currentPoint = 0;//默认第一个坐标
 | 
			
		||||
//        imageViews[currentPoint].setImageResource(R.drawable.ic_launcher);
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    //OnPageChangeListener接口要实现的三个方法
 | 
			
		||||
    /*    onPageScrollStateChanged(int state)
 | 
			
		||||
     此方法是在状态改变的时候调用,其中state这个参数有三种状态:
 | 
			
		||||
     SCROLL_STATE_DRAGGING(1)表示用户手指“按在屏幕上并且开始拖动”的状态
 | 
			
		||||
     (手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。)
 | 
			
		||||
     SCROLL_STATE_IDLE(0)滑动动画做完的状态。
 | 
			
		||||
     SCROLL_STATE_SETTLING(2)在“手指离开屏幕”的状态。*/
 | 
			
		||||
    // OnPageChangeListener接口实现
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPageScrollStateChanged(int state) {
 | 
			
		||||
    public void onPageScrollStateChanged(int state) {}
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /*    onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
 | 
			
		||||
     当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
 | 
			
		||||
 | 
			
		||||
     position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。
 | 
			
		||||
     positionOffset:当前页面偏移的百分比
 | 
			
		||||
     positionOffsetPixels:当前页面偏移的像素位置*/
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
 | 
			
		||||
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /*    onPageSelected(int position)
 | 
			
		||||
     此方法是页面滑动完后得到调用,position是你当前选中的页面的Position(位置编号)
 | 
			
		||||
     (从A滑动到B,就是B的position)*/
 | 
			
		||||
    public void onPageSelected(int position) {
 | 
			
		||||
 | 
			
		||||
//        ImageView preView = imageViews[currentPoint];
 | 
			
		||||
//        preView.setImageResource(R.drawable.ic_launcher);
 | 
			
		||||
//        ImageView currView = imageViews[position];
 | 
			
		||||
//        currView.setImageResource(R.drawable.ic_launcher);
 | 
			
		||||
//        currentPoint = position;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //小圆点点击事件
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onClick(View v) {
 | 
			
		||||
        // TODO Auto-generated method stub
 | 
			
		||||
        //通过getTag(),可以判断是哪个控件
 | 
			
		||||
//        int i = (Integer) v.getTag();
 | 
			
		||||
//        viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况
 | 
			
		||||
    }
 | 
			
		||||
    public void onPageSelected(int position) {}
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onClick(View v) {}
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onPostCreate(savedInstanceState);
 | 
			
		||||
        //setSubTitle("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class MyPhoneStateListener extends PhoneStateListener {
 | 
			
		||||
@@ -336,109 +219,31 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
 | 
			
		||||
        LogUtils.d(TAG, "onDestroy() SOS");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 处理传入的 Intent 数据
 | 
			
		||||
    //
 | 
			
		||||
//    boolean prosessIntents(Intent intent) {
 | 
			
		||||
//        if (intent == null 
 | 
			
		||||
//            || intent.getAction() == null
 | 
			
		||||
//            || intent.getAction().equals(""))
 | 
			
		||||
//            return false;
 | 
			
		||||
//
 | 
			
		||||
//        if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
 | 
			
		||||
//            try {
 | 
			
		||||
//                WinBoLLActivity clazzActivity = UnitTestActivity.class.newInstance();
 | 
			
		||||
//                String tag = clazzActivity.getTag();
 | 
			
		||||
//                LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
 | 
			
		||||
//                Intent subIntent = new Intent(this, UnitTestActivity.class);
 | 
			
		||||
//                subIntent.setAction(intent.getAction());
 | 
			
		||||
//                File file = new File(getCacheDir(), UUID.randomUUID().toString());
 | 
			
		||||
//                //取出文件uri
 | 
			
		||||
//                Uri uri = intent.getData();
 | 
			
		||||
//                if (uri == null) {
 | 
			
		||||
//                    uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
 | 
			
		||||
//                }
 | 
			
		||||
//                //获取文件真实地址
 | 
			
		||||
//                String szSrcPath = UriUtils.getFileFromUri(getApplication(), uri);
 | 
			
		||||
//                if (TextUtils.isEmpty(szSrcPath)) {
 | 
			
		||||
//                    return false;
 | 
			
		||||
//                }
 | 
			
		||||
//
 | 
			
		||||
//                Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
 | 
			
		||||
//                //startWinBoLLActivity(subIntent, tag);
 | 
			
		||||
//                WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, subIntent, UnitTestActivity.class);
 | 
			
		||||
//            } catch (IllegalAccessException | InstantiationException | IOException e) {
 | 
			
		||||
//                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
//                // 函数处理异常返回失败
 | 
			
		||||
//                return false;
 | 
			
		||||
//            }
 | 
			
		||||
//        } else {
 | 
			
		||||
//            LogUtils.d(TAG, "prosessIntents|" + intent.getAction() + "|yet");
 | 
			
		||||
//            return false;
 | 
			
		||||
//        }
 | 
			
		||||
//        return true;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public String getTag() {
 | 
			
		||||
//        return TAG;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
//    @Override
 | 
			
		||||
//    public void onBackPressed() {
 | 
			
		||||
//        exit();
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    void exit() {
 | 
			
		||||
//        YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
 | 
			
		||||
//
 | 
			
		||||
//            @Override
 | 
			
		||||
//            public void onYes() {
 | 
			
		||||
//                WinBoLLActivityManager.getInstance(getApplicationContext()).finishAll();
 | 
			
		||||
//            }
 | 
			
		||||
//
 | 
			
		||||
//            @Override
 | 
			
		||||
//            public void onNo() {
 | 
			
		||||
//            }
 | 
			
		||||
//        };
 | 
			
		||||
//        YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_main, menu);
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if (item.getItemId() == R.id.item_settings) {
 | 
			
		||||
            Intent intent = new Intent(this, SettingsActivity.class);
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
            //WinBoLLActivityManager.getInstance(this).startWinBoLLActivity(this, CallActivity.class);
 | 
			
		||||
        }
 | 
			
		||||
//        } else 
 | 
			
		||||
//        if (item.getItemId() == R.id.item_exit) {
 | 
			
		||||
//            exit();
 | 
			
		||||
//            return true;
 | 
			
		||||
//        }
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Android M 及以上检查是否是系统默认电话应用
 | 
			
		||||
     * 检查是否是系统默认电话应用
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isDefaultPhoneCallApp() {
 | 
			
		||||
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
			
		||||
            TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
 | 
			
		||||
            if (manger != null && manger.getDefaultDialerPackage() != null) {
 | 
			
		||||
                return manger.getDefaultDialerPackage().equals(getPackageName());
 | 
			
		||||
@@ -452,35 +257,22 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
 | 
			
		||||
        if (manager == null) return false;
 | 
			
		||||
 | 
			
		||||
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
 | 
			
		||||
            Integer.MAX_VALUE)) {
 | 
			
		||||
			Integer.MAX_VALUE)) {
 | 
			
		||||
            if (serviceClass.getName().equals(service.service.getClassName())) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 | 
			
		||||
//        switch (resultCode) {
 | 
			
		||||
//            case REQUEST_HOME_ACTIVITY : {
 | 
			
		||||
//                    LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
 | 
			
		||||
//                    break;
 | 
			
		||||
//                }
 | 
			
		||||
//            case REQUEST_ABOUT_ACTIVITY : {
 | 
			
		||||
//                    LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
 | 
			
		||||
//                    break;
 | 
			
		||||
//                }
 | 
			
		||||
//            default : {
 | 
			
		||||
//                    super.onActivityResult(requestCode, resultCode, data);
 | 
			
		||||
//                }
 | 
			
		||||
//        }
 | 
			
		||||
        if (requestCode == DIALER_REQUEST_CODE) {
 | 
			
		||||
            if (resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
 | 
			
		||||
                               Toast.LENGTH_SHORT).show();
 | 
			
		||||
							   Toast.LENGTH_SHORT).show();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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("APP");
 | 
			
		||||
        appInfo.setAppDescription("这是可以根据正则表达式匹配拦截骚扰电话的手机拨号应用。");
 | 
			
		||||
        appInfo.setAppGitName("APPBase");
 | 
			
		||||
        appInfo.setAppGitOwner("Studio");
 | 
			
		||||
        appInfo.setAppGitAPPBranch(szBranchName);
 | 
			
		||||
        appInfo.setAppGitAPPSubProjectFolder(szBranchName);
 | 
			
		||||
        appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=4&extra=page%3D1");
 | 
			
		||||
        appInfo.setAppAPKName("Contacts");
 | 
			
		||||
        appInfo.setAppAPKFolderName("Contacts");
 | 
			
		||||
        return new AboutView(mContext, appInfo);
 | 
			
		||||
 
 | 
			
		||||
@@ -198,6 +198,9 @@ 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();
 | 
			
		||||
@@ -207,6 +210,7 @@ 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() {
 | 
			
		||||
@@ -243,6 +247,9 @@ 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) {
 | 
			
		||||
@@ -330,4 +337,8 @@ 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,13 +5,18 @@ 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;
 | 
			
		||||
@@ -35,6 +40,10 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV
 | 
			
		||||
        this.mContactUtils = ContactUtils.getInstance(mContext);
 | 
			
		||||
        this.callLogList = callLogList;
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	public void relaodContacts() {
 | 
			
		||||
		this.mContactUtils.relaodContacts();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -47,6 +56,38 @@ 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,19 +5,25 @@ 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.Button;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
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;
 | 
			
		||||
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> {
 | 
			
		||||
 | 
			
		||||
@@ -26,8 +32,10 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV
 | 
			
		||||
    private static final int REQUEST_CALL_PHONE = 1;
 | 
			
		||||
 | 
			
		||||
    private List<ContactModel> contactList;
 | 
			
		||||
	Context mContext;
 | 
			
		||||
 | 
			
		||||
    public ContactAdapter(List<ContactModel> contactList) {
 | 
			
		||||
    public ContactAdapter(Context context, List<ContactModel> contactList) {
 | 
			
		||||
		mContext = context;
 | 
			
		||||
        this.contactList = contactList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +49,37 @@ 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());
 | 
			
		||||
 | 
			
		||||
@@ -69,12 +108,14 @@ 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,6 +7,7 @@ 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;
 | 
			
		||||
@@ -20,6 +21,7 @@ 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;
 | 
			
		||||
@@ -60,6 +62,10 @@ 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
 | 
			
		||||
@@ -215,16 +221,22 @@ 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);
 | 
			
		||||
            //tvRuleText = itemView.findViewById(R.id.ruletext_tv);
 | 
			
		||||
            tvRuleText = new TextView(itemView.getContext());
 | 
			
		||||
			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());
 | 
			
		||||
            scrollView.setContentWidth(parent.getWidth());
 | 
			
		||||
            //scrollView.setContentWidth(600);
 | 
			
		||||
            scrollView.addContentLayout(tvRuleText);
 | 
			
		||||
            scrollView.addContentLayout(viewContent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@@ -243,5 +255,9 @@ public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.V
 | 
			
		||||
            buttonConfirm = itemView.findViewById(R.id.button_confirm);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	private void setCheckBoxTouchListener(CheckBox checkBox) {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
package cc.winboll.studio.contacts.beans;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/02/26 13:37:00
 | 
			
		||||
 * @Describe ContactModel
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 联系人信息数据模型
 | 
			
		||||
 */
 | 
			
		||||
import net.sourceforge.pinyin4j.PinyinHelper;
 | 
			
		||||
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
 | 
			
		||||
@@ -18,13 +18,18 @@ public class ContactModel {
 | 
			
		||||
    private String name;
 | 
			
		||||
    private String number;
 | 
			
		||||
    private String pinyin;
 | 
			
		||||
    // 新增:存储姓名的拼音首字母(如"啊牛"→"an")
 | 
			
		||||
    private String pinyinFirstLetter;
 | 
			
		||||
 | 
			
		||||
    public ContactModel(String name, String number) {
 | 
			
		||||
        this.name = name;
 | 
			
		||||
        this.number = number.replaceAll("\\s", "");
 | 
			
		||||
        this.pinyin = convertToPinyin(name);
 | 
			
		||||
        // 初始化时生成拼音首字母
 | 
			
		||||
        this.pinyinFirstLetter = convertToPinyinFirstLetter(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 原方法:转换为全拼(如"啊牛"→"aniu")
 | 
			
		||||
    private String convertToPinyin(String chinese) {
 | 
			
		||||
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
 | 
			
		||||
        format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
 | 
			
		||||
@@ -33,22 +38,55 @@ public class ContactModel {
 | 
			
		||||
        StringBuilder pinyin = new StringBuilder();
 | 
			
		||||
        for (int i = 0; i < chinese.length(); i++) {
 | 
			
		||||
            char ch = chinese.charAt(i);
 | 
			
		||||
            if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) {
 | 
			
		||||
            if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字
 | 
			
		||||
                try {
 | 
			
		||||
                    String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
 | 
			
		||||
                    if (pinyinArray != null) {
 | 
			
		||||
                        pinyin.append(pinyinArray[0]);
 | 
			
		||||
                    if (pinyinArray != null && pinyinArray.length > 0) {
 | 
			
		||||
                        pinyin.append(pinyinArray[0]); // 取第一个拼音(多音字默认首选项)
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (BadHanyuPinyinOutputFormatCombination e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                pinyin.append(ch);
 | 
			
		||||
                pinyin.append(ch); // 非汉字直接拼接(如字母、数字、符号)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return pinyin.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 新增:转换为拼音首字母(如"啊牛"→"an")
 | 
			
		||||
    private String convertToPinyinFirstLetter(String chinese) {
 | 
			
		||||
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
 | 
			
		||||
        format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
 | 
			
		||||
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
 | 
			
		||||
 | 
			
		||||
        StringBuilder firstLetters = new StringBuilder();
 | 
			
		||||
        for (int i = 0; i < chinese.length(); i++) {
 | 
			
		||||
            char ch = chinese.charAt(i);
 | 
			
		||||
            if (Character.toString(ch).matches("[\\u4e00-\\u9fa5]")) { // 仅处理汉字
 | 
			
		||||
                try {
 | 
			
		||||
                    String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
 | 
			
		||||
                    if (pinyinArray != null && pinyinArray.length > 0) {
 | 
			
		||||
                        // 取拼音的第一个字母(如"a"、"niu"→"a"、"n")
 | 
			
		||||
                        firstLetters.append(pinyinArray[0].charAt(0));
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (BadHanyuPinyinOutputFormatCombination e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // 非汉字可根据需求处理:此处保留原字符(如"李3"→"l3","张A"→"za")
 | 
			
		||||
                firstLetters.append(ch);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return firstLetters.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 新增:获取拼音首字母
 | 
			
		||||
    public String getPinyinFirstLetter() {
 | 
			
		||||
        return pinyinFirstLetter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 原有getter方法
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import cc.winboll.studio.contacts.dun.Rules;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileFilter;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
@@ -44,7 +45,7 @@ public class TomCat {
 | 
			
		||||
        }
 | 
			
		||||
        return _TomCat;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public String getDefaultBobulltoonUrl() {
 | 
			
		||||
        return mContext.getString(R.string.default_bobulltoon_url);
 | 
			
		||||
    }
 | 
			
		||||
@@ -123,7 +124,7 @@ public class TomCat {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 更新新文件
 | 
			
		||||
            if(downloadAndExtractZip(zipUrl, destinationFolder)) {
 | 
			
		||||
            if (downloadAndExtractZip(zipUrl, destinationFolder)) {
 | 
			
		||||
                LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
@@ -154,12 +155,81 @@ public class TomCat {
 | 
			
		||||
    File getWorkingFolder() {
 | 
			
		||||
        return mContext.getExternalFilesDir(TAG);
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	public File getBoBullToonDataFolder() {
 | 
			
		||||
		File fCheckRoot = getWorkingFolder();
 | 
			
		||||
		if (fCheckRoot == null || !fCheckRoot.exists()) {
 | 
			
		||||
			return fCheckRoot;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 递归查找符合条件的文件夹
 | 
			
		||||
		File targetFolder = findTargetFolder(fCheckRoot);
 | 
			
		||||
		return targetFolder != null ? targetFolder : fCheckRoot;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 递归查找同时包含LICENSE和README.md文件的文件夹
 | 
			
		||||
	 */
 | 
			
		||||
	private File findTargetFolder(File currentFolder) {
 | 
			
		||||
		// 检查当前文件夹是否符合条件
 | 
			
		||||
		if (hasRequiredFiles(currentFolder)) {
 | 
			
		||||
			return currentFolder;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 查找子文件夹(Java 7不支持方法引用,用匿名内部类过滤)
 | 
			
		||||
		File[] subFolders = currentFolder.listFiles(new FileFilter() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public boolean accept(File file) {
 | 
			
		||||
					return file.isDirectory(); // 仅保留子文件夹
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		if (subFolders != null) {
 | 
			
		||||
			for (File subFolder : subFolders) {
 | 
			
		||||
				File result = findTargetFolder(subFolder);
 | 
			
		||||
				if (result != null) {
 | 
			
		||||
					return result;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 检查文件夹中是否同时存在LICENSE和README.md文件
 | 
			
		||||
	 */
 | 
			
		||||
	private boolean hasRequiredFiles(File folder) {
 | 
			
		||||
		if (folder == null || !folder.isDirectory()) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 检查两个文件是否同时存在且均为文件(非文件夹)
 | 
			
		||||
		File licenseFile = new File(folder, "LICENSE");
 | 
			
		||||
		File readmeFile = new File(folder, "README.md");
 | 
			
		||||
 | 
			
		||||
		return licenseFile.exists() && licenseFile.isFile()
 | 
			
		||||
			&& readmeFile.exists() && readmeFile.isFile();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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");
 | 
			
		||||
        File fBoBullToon = getBoBullToonDataFolder();
 | 
			
		||||
        if (fBoBullToon.exists()) {
 | 
			
		||||
            LogUtils.d(TAG, String.format("getWorkingFolder() %s", getWorkingFolder()));
 | 
			
		||||
            LogUtils.d(TAG, String.format("getBoBullToonDataFolder() %s", getWorkingFolder()));
 | 
			
		||||
            for (File userFolder : fBoBullToon.listFiles()) {
 | 
			
		||||
                if (userFolder.isDirectory()) {
 | 
			
		||||
                    for (File recordFile : userFolder.listFiles()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -145,6 +145,14 @@ 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) {
 | 
			
		||||
@@ -158,14 +166,6 @@ 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++) {
 | 
			
		||||
 
 | 
			
		||||
@@ -161,4 +161,12 @@ public class CallLogFragment extends Fragment {
 | 
			
		||||
            _CallLogFragment.triggerUpdate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public void onResume() {
 | 
			
		||||
		super.onResume();
 | 
			
		||||
		//ToastUtils.show("onResume");
 | 
			
		||||
		callLogAdapter.relaodContacts();
 | 
			
		||||
		readCallLog();  // 窗口回显时更新通话记录
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,18 @@
 | 
			
		||||
package cc.winboll.studio.contacts.fragments;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/02/20 12:57:50
 | 
			
		||||
 * @Describe 联系人
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 联系人视图
 | 
			
		||||
 */
 | 
			
		||||
import android.Manifest;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.database.Cursor;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Looper;
 | 
			
		||||
import android.provider.ContactsContract;
 | 
			
		||||
import android.text.Editable;
 | 
			
		||||
import android.text.TextWatcher;
 | 
			
		||||
@@ -27,24 +30,39 @@ import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
import cc.winboll.studio.contacts.R;
 | 
			
		||||
import cc.winboll.studio.contacts.adapters.ContactAdapter;
 | 
			
		||||
import cc.winboll.studio.contacts.beans.ContactModel;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.ExecutorService;
 | 
			
		||||
import java.util.concurrent.Executors;
 | 
			
		||||
 | 
			
		||||
public class ContactsFragment extends Fragment {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "ContactsFragment";
 | 
			
		||||
 | 
			
		||||
    private static final String ARG_PAGE = "ARG_PAGE";
 | 
			
		||||
    private int mPage;
 | 
			
		||||
 | 
			
		||||
    private static final int REQUEST_READ_CONTACTS = 1;
 | 
			
		||||
 | 
			
		||||
    private int mPage;
 | 
			
		||||
    private RecyclerView recyclerView;
 | 
			
		||||
    private ContactAdapter contactAdapter;
 | 
			
		||||
    private List<ContactModel> contactList = new ArrayList<>();
 | 
			
		||||
    private List<ContactModel> originalContactList = new ArrayList<>();
 | 
			
		||||
    private EditText searchEditText;
 | 
			
		||||
    private Button btnDial;
 | 
			
		||||
    private boolean isViewInitialized = false; // 标记视图是否已初始化
 | 
			
		||||
 | 
			
		||||
    // 静态缓存:全局复用联系人数据
 | 
			
		||||
    private static List<ContactModel> sCachedOriginalList = new ArrayList<ContactModel>();
 | 
			
		||||
    private static List<ContactModel> sCachedFilteredList = new ArrayList<ContactModel>();
 | 
			
		||||
 | 
			
		||||
    // 当前页面数据容器
 | 
			
		||||
    private List<ContactModel> contactList = new ArrayList<ContactModel>();
 | 
			
		||||
    private List<ContactModel> originalContactList = new ArrayList<ContactModel>();
 | 
			
		||||
 | 
			
		||||
    // 异步工具
 | 
			
		||||
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
 | 
			
		||||
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
 | 
			
		||||
    private boolean isDataLoaded = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static ContactsFragment newInstance(int page) {
 | 
			
		||||
        Bundle args = new Bundle();
 | 
			
		||||
@@ -65,103 +83,272 @@ public class ContactsFragment extends Fragment {
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 | 
			
		||||
        return inflater.inflate(R.layout.fragment_contacts, container, false);
 | 
			
		||||
        // 加载布局(已移除进度条相关代码)
 | 
			
		||||
        View view = inflater.inflate(R.layout.fragment_contacts, container, false);
 | 
			
		||||
        return view;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState);
 | 
			
		||||
        recyclerView = view.findViewById(R.id.contacts_recycler_view);
 | 
			
		||||
        // 初始化RecyclerView
 | 
			
		||||
        recyclerView = (RecyclerView) view.findViewById(R.id.contacts_recycler_view);
 | 
			
		||||
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
 | 
			
		||||
        contactAdapter = new ContactAdapter(contactList);
 | 
			
		||||
        contactList = new ArrayList<ContactModel>();
 | 
			
		||||
        contactAdapter = new ContactAdapter(getContext(), contactList);
 | 
			
		||||
        recyclerView.setAdapter(contactAdapter);
 | 
			
		||||
        // 初始隐藏列表,数据加载后显示
 | 
			
		||||
        recyclerView.setVisibility(View.GONE);
 | 
			
		||||
 | 
			
		||||
        searchEditText = view.findViewById(R.id.search_edit_text);
 | 
			
		||||
        searchEditText.addTextChangedListener(new TextWatcher() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
                }
 | 
			
		||||
        // 绑定搜索框和拨号按钮
 | 
			
		||||
        searchEditText = (EditText) view.findViewById(R.id.search_edit_text);
 | 
			
		||||
        btnDial = (Button) view.findViewById(R.id.btn_dial);
 | 
			
		||||
        // 初始隐藏搜索相关控件,延迟到首次可见时显示
 | 
			
		||||
        searchEditText.setVisibility(View.GONE);
 | 
			
		||||
        btnDial.setVisibility(View.GONE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
                    filterContacts(s.toString());
 | 
			
		||||
                }
 | 
			
		||||
    // 首次可见时初始化资源
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        if (!isViewInitialized) {
 | 
			
		||||
            initSearchAndDial(); // 初始化搜索和拨号功能
 | 
			
		||||
            checkContactPermission(); // 检查权限并加载数据
 | 
			
		||||
            isViewInitialized = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void afterTextChanged(Editable s) {
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    // 初始化搜索框和拨号按钮
 | 
			
		||||
    private void initSearchAndDial() {
 | 
			
		||||
        // 显示搜索相关控件
 | 
			
		||||
        searchEditText.setVisibility(View.VISIBLE);
 | 
			
		||||
        btnDial.setVisibility(View.VISIBLE);
 | 
			
		||||
 | 
			
		||||
        // 搜索框防抖监听
 | 
			
		||||
        searchEditText.addTextChangedListener(new DebounceTextWatcher(300) {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onDebounceTextChanged(String query) {
 | 
			
		||||
					filterContacts(query);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 拨号按钮点击事件
 | 
			
		||||
        btnDial.setOnClickListener(new View.OnClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onClick(View v) {
 | 
			
		||||
					String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", "");
 | 
			
		||||
					if (phoneNumber.isEmpty()) {
 | 
			
		||||
						ToastUtils.show("请输入号码");
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
					Intent intent = new Intent(Intent.ACTION_CALL);
 | 
			
		||||
					intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
 | 
			
		||||
					intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
					startActivity(intent);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 权限检查
 | 
			
		||||
    private void checkContactPermission() {
 | 
			
		||||
        if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
 | 
			
		||||
            ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
 | 
			
		||||
        } else {
 | 
			
		||||
            readContacts();
 | 
			
		||||
            loadContacts();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Button btnDial = view.findViewById(R.id.btn_dial);
 | 
			
		||||
        btnDial.setOnClickListener(new View.OnClickListener(){
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onClick(View p1) {
 | 
			
		||||
 | 
			
		||||
                    String phoneNumber = searchEditText.getText().toString().replaceAll("\\s", "");
 | 
			
		||||
                    //phoneNumber = "+8616769764848";
 | 
			
		||||
                    ToastUtils.show(phoneNumber);
 | 
			
		||||
                    Intent intent = new Intent(Intent.ACTION_CALL);
 | 
			
		||||
                    intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
 | 
			
		||||
                    // 添加 FLAG_ACTIVITY_NEW_TASK 标志
 | 
			
		||||
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
                    startActivity(intent);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 加载联系人(延迟到首次可见时)
 | 
			
		||||
    private void loadContacts() {
 | 
			
		||||
        // 若有缓存,直接复用
 | 
			
		||||
        if (!sCachedOriginalList.isEmpty() && !sCachedFilteredList.isEmpty()) {
 | 
			
		||||
            originalContactList.clear();
 | 
			
		||||
            originalContactList.addAll(sCachedOriginalList);
 | 
			
		||||
            contactList.clear();
 | 
			
		||||
            contactList.addAll(sCachedFilteredList);
 | 
			
		||||
            contactAdapter.notifyDataSetChanged();
 | 
			
		||||
            recyclerView.setVisibility(View.VISIBLE); // 显示列表
 | 
			
		||||
            isDataLoaded = true;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 无缓存时异步加载
 | 
			
		||||
        if (!isDataLoaded) {
 | 
			
		||||
            recyclerView.setVisibility(View.GONE); // 加载中隐藏列表
 | 
			
		||||
 | 
			
		||||
            executor.execute(new Runnable() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void run() {
 | 
			
		||||
						// 子线程读取联系人
 | 
			
		||||
						final List<ContactModel> tempList = readContactsInBackground();
 | 
			
		||||
 | 
			
		||||
						// 主线程更新UI
 | 
			
		||||
						mainHandler.post(new Runnable() {
 | 
			
		||||
								@Override
 | 
			
		||||
								public void run() {
 | 
			
		||||
									// 更新缓存
 | 
			
		||||
									sCachedOriginalList.clear();
 | 
			
		||||
									sCachedOriginalList.addAll(tempList);
 | 
			
		||||
									sCachedFilteredList.clear();
 | 
			
		||||
									sCachedFilteredList.addAll(tempList);
 | 
			
		||||
 | 
			
		||||
									// 更新当前列表
 | 
			
		||||
									originalContactList.clear();
 | 
			
		||||
									originalContactList.addAll(sCachedOriginalList);
 | 
			
		||||
									contactList.clear();
 | 
			
		||||
									contactList.addAll(sCachedFilteredList);
 | 
			
		||||
									contactAdapter.notifyDataSetChanged();
 | 
			
		||||
									LogUtils.d(TAG, String.format("联系人加载完成,共%d条数据", contactList.size()));
 | 
			
		||||
 | 
			
		||||
									// 数据加载后显示列表
 | 
			
		||||
									recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
									isDataLoaded = true;
 | 
			
		||||
								}
 | 
			
		||||
							});
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 子线程读取联系人
 | 
			
		||||
    private List<ContactModel> readContactsInBackground() {
 | 
			
		||||
        List<ContactModel> tempList = new ArrayList<ContactModel>();
 | 
			
		||||
        Cursor cursor = null;
 | 
			
		||||
        try {
 | 
			
		||||
            // 查询联系人姓名和号码
 | 
			
		||||
            cursor = requireContext().getContentResolver().query(
 | 
			
		||||
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
 | 
			
		||||
                new String[]{
 | 
			
		||||
                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
 | 
			
		||||
                    ContactsContract.CommonDataKinds.Phone.NUMBER
 | 
			
		||||
                },
 | 
			
		||||
                null,
 | 
			
		||||
                null,
 | 
			
		||||
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if (cursor != null && cursor.moveToFirst()) {
 | 
			
		||||
                int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
 | 
			
		||||
                int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
 | 
			
		||||
 | 
			
		||||
                do {
 | 
			
		||||
                    String name = cursor.getString(nameIndex);
 | 
			
		||||
                    String number = cursor.getString(numberIndex).replaceAll("\\s", ""); // 去除空格
 | 
			
		||||
                    tempList.add(new ContactModel(name, number));
 | 
			
		||||
                } while (cursor.moveToNext());
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            LogUtils.d(TAG, "读取联系人失败:" + e);
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (cursor != null) {
 | 
			
		||||
                cursor.close(); // 关闭游标,避免内存泄漏
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return tempList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 过滤联系人
 | 
			
		||||
    private void filterContacts(String query) {
 | 
			
		||||
        contactList.clear();
 | 
			
		||||
        if (query.isEmpty()) {
 | 
			
		||||
            contactList.addAll(originalContactList);
 | 
			
		||||
            sCachedFilteredList.clear();
 | 
			
		||||
            sCachedFilteredList.addAll(originalContactList);
 | 
			
		||||
        } else {
 | 
			
		||||
            String lowerQuery = query.toLowerCase();
 | 
			
		||||
            for (ContactModel contact : originalContactList) {
 | 
			
		||||
                // 匹配姓名、全拼、简拼、号码
 | 
			
		||||
                boolean matchName = contact.getName().toLowerCase().contains(lowerQuery);
 | 
			
		||||
                boolean matchPinyin = contact.getPinyin().toLowerCase().contains(lowerQuery);
 | 
			
		||||
                boolean matchFirstLetter = contact.getPinyinFirstLetter().toLowerCase().contains(lowerQuery);
 | 
			
		||||
                boolean matchNumber = contact.getNumber().contains(lowerQuery);
 | 
			
		||||
 | 
			
		||||
                if (matchName || matchPinyin || matchFirstLetter || matchNumber) {
 | 
			
		||||
                    contactList.add(contact);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            sCachedFilteredList.clear();
 | 
			
		||||
            sCachedFilteredList.addAll(contactList);
 | 
			
		||||
        }
 | 
			
		||||
        contactAdapter.notifyDataSetChanged();
 | 
			
		||||
        // 过滤后确保列表可见
 | 
			
		||||
        recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 权限回调
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
 | 
			
		||||
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 | 
			
		||||
        if (requestCode == REQUEST_READ_CONTACTS) {
 | 
			
		||||
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 | 
			
		||||
                readContacts();
 | 
			
		||||
                loadContacts(); // 授权后加载联系人
 | 
			
		||||
            } else {
 | 
			
		||||
                ToastUtils.show("请授予联系人权限以查看联系人列表");
 | 
			
		||||
                recyclerView.setVisibility(View.VISIBLE); // 显示空列表
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void readContacts() {
 | 
			
		||||
        contactList.clear();
 | 
			
		||||
        originalContactList.clear();
 | 
			
		||||
        Cursor cursor = requireContext().getContentResolver().query(
 | 
			
		||||
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
 | 
			
		||||
    // 防抖TextWatcher(Java 7实现)
 | 
			
		||||
    public abstract static class DebounceTextWatcher implements TextWatcher {
 | 
			
		||||
        private final long debounceDelay;
 | 
			
		||||
        private Handler handler = new Handler(Looper.getMainLooper());
 | 
			
		||||
        private Runnable pendingRunnable;
 | 
			
		||||
 | 
			
		||||
        if (cursor != null) {
 | 
			
		||||
            while (cursor.moveToNext()) {
 | 
			
		||||
                String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
 | 
			
		||||
                String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
 | 
			
		||||
                ContactModel contact = new ContactModel(name, number);
 | 
			
		||||
                contactList.add(contact);
 | 
			
		||||
                originalContactList.add(contact);
 | 
			
		||||
            }
 | 
			
		||||
            cursor.close();
 | 
			
		||||
            contactAdapter.notifyDataSetChanged();
 | 
			
		||||
        public DebounceTextWatcher(long debounceDelay) {
 | 
			
		||||
            this.debounceDelay = debounceDelay;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void filterContacts(String query) {
 | 
			
		||||
        contactList.clear();
 | 
			
		||||
        if (query.isEmpty()) {
 | 
			
		||||
            contactList.addAll(originalContactList);
 | 
			
		||||
        } else {
 | 
			
		||||
            for (ContactModel contact : originalContactList) {
 | 
			
		||||
                if (contact.getName().toLowerCase().contains(query.toLowerCase()) ||
 | 
			
		||||
                    contact.getPinyin().toLowerCase().contains(query.toLowerCase()) ||
 | 
			
		||||
                    contact.getNumber().toLowerCase().contains(query.toLowerCase())) {
 | 
			
		||||
                    contactList.add(contact);
 | 
			
		||||
        @Override
 | 
			
		||||
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
            // 无需处理
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onTextChanged(final CharSequence s, int start, int before, int count) {
 | 
			
		||||
            // 移除之前的延迟任务
 | 
			
		||||
            if (pendingRunnable != null) {
 | 
			
		||||
                handler.removeCallbacks(pendingRunnable);
 | 
			
		||||
            }
 | 
			
		||||
            // 延迟执行过滤
 | 
			
		||||
            pendingRunnable = new Runnable() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void run() {
 | 
			
		||||
                    onDebounceTextChanged(s.toString());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            };
 | 
			
		||||
            handler.postDelayed(pendingRunnable, debounceDelay);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void afterTextChanged(Editable s) {
 | 
			
		||||
            // 无需处理
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 抽象方法:防抖后的回调
 | 
			
		||||
        public abstract void onDebounceTextChanged(String query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 资源释放
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        executor.shutdown(); // 关闭线程池
 | 
			
		||||
        mainHandler.removeCallbacksAndMessages(null); // 清除未执行任务
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Fragment隐藏/显示时的处理
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onHiddenChanged(boolean hidden) {
 | 
			
		||||
        super.onHiddenChanged(hidden);
 | 
			
		||||
        if (!hidden && isDataLoaded) {
 | 
			
		||||
            // 复用缓存数据并显示列表
 | 
			
		||||
            contactList.clear();
 | 
			
		||||
            contactList.addAll(sCachedFilteredList);
 | 
			
		||||
            contactAdapter.notifyDataSetChanged();
 | 
			
		||||
            recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
        }
 | 
			
		||||
        contactAdapter.notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
package cc.winboll.studio.contacts.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/03/06 21:08:16
 | 
			
		||||
 * @Describe ContactUtils
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 联系人工具集
 | 
			
		||||
 */
 | 
			
		||||
import android.content.ContentResolver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
 
 | 
			
		||||
@@ -47,8 +47,8 @@ public class LeftScrollView extends HorizontalScrollView {
 | 
			
		||||
        init();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void addContentLayout(TextView textView) {
 | 
			
		||||
        contentLayout.addView(textView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
 | 
			
		||||
    public void addContentLayout(View viewContent) {
 | 
			
		||||
        contentLayout.addView(viewContent, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setContentWidth(int contentWidth) {
 | 
			
		||||
 
 | 
			
		||||
@@ -269,6 +269,19 @@
 | 
			
		||||
 | 
			
		||||
			</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"
 | 
			
		||||
@@ -287,6 +300,12 @@
 | 
			
		||||
				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,8 +15,10 @@
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Test Main"
 | 
			
		||||
			android:onClick="onTestMain"/>
 | 
			
		||||
			android:text="Add Demo Rules(While size is 0) and Test"
 | 
			
		||||
			android:onClick="onTestMain"
 | 
			
		||||
			android:textSize="10sp"
 | 
			
		||||
			android:textAllCaps="false"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -43,7 +45,8 @@
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Test Phone"
 | 
			
		||||
			android:onClick="onTestPhone"/>
 | 
			
		||||
			android:onClick="onTestPhone"
 | 
			
		||||
			android:textAllCaps="false"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,8 @@
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/itemcontactLinearLayout1">
 | 
			
		||||
 | 
			
		||||
		<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"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
<?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>
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
<?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>
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<?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>
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<?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">http://10.8.0.12:3000/Studio/BoBullToon/archive/main.zip</string>
 | 
			
		||||
    <string name="default_bobulltoon_url">https://gitee.com/zhangsken/bobulltoon/repository/archive/main.zip</string>
 | 
			
		||||
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -42,23 +42,24 @@ android {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    compileOptions {
 | 
			
		||||
    /*compileOptions {
 | 
			
		||||
        sourceCompatibility JavaVersion.VERSION_11
 | 
			
		||||
        targetCompatibility JavaVersion.VERSION_11
 | 
			
		||||
    }
 | 
			
		||||
    }*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
	api project(':libjc')
 | 
			
		||||
	api 'cc.winboll.studio:libaes:15.9.1'
 | 
			
		||||
	api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
	api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
	
 | 
			
		||||
    // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
 | 
			
		||||
    //implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
 | 
			
		||||
    implementation 'org.bouncycastle:bcprov-jdk15to18:1.69'
 | 
			
		||||
    implementation 'org.bouncycastle:bcpkix-jdk15to18:1.69'
 | 
			
		||||
 | 
			
		||||
	api project(':libjc')
 | 
			
		||||
    api 'androidx.appcompat:appcompat:1.0.0'
 | 
			
		||||
	api 'com.google.android.material:material:1.0.0'
 | 
			
		||||
    
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:9.1.0'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:1.0.3'
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Fri Jan 10 22:03:57 GMT 2025
 | 
			
		||||
#Tue Jun 24 11:17:30 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
libraryProject=libjc
 | 
			
		||||
baseVersion=1.0
 | 
			
		||||
publishVersion=1.0.0
 | 
			
		||||
buildCount=133
 | 
			
		||||
buildCount=135
 | 
			
		||||
baseBetaVersion=1.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,10 @@ import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import cc.winboll.studio.jc.R;
 | 
			
		||||
import cc.winboll.studio.libapputils.log.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libjc.JAR_RUNNING_MODE;
 | 
			
		||||
import cc.winboll.studio.libjc.JCMainThread;
 | 
			
		||||
import cc.winboll.studio.libjc.net.JCSocketClient;
 | 
			
		||||
import cc.winboll.studio.libjc.util.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libjc.Main;
 | 
			
		||||
 | 
			
		||||
final public class MainActivity extends Activity implements JCMainThread.OnMessageListener {
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +77,7 @@ final public class MainActivity extends Activity implements JCMainThread.OnMessa
 | 
			
		||||
        // 启动主线程
 | 
			
		||||
        _JCMainThread = JCMainThread.getInstance(getPackageName());
 | 
			
		||||
        _JCMainThread.setOnLogListener(this);
 | 
			
		||||
        _JCMainThread.setRunningMode(JAR_RUNNING_MODE.JC);
 | 
			
		||||
        //_JCMainThread.setRunningMode(Main.JAR_RUNNING_MODE.JC);
 | 
			
		||||
        _JCMainThread.start();
 | 
			
		||||
 | 
			
		||||
        // 设置 WinBoll 应用 UI 类型
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    
 | 
			
		||||
    // 吐司类库
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue Jun 03 19:16:41 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Sat Jun 28 12:59:30 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.1
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.2
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
 
 | 
			
		||||
@@ -107,7 +107,7 @@ public class AboutView extends LinearLayout {
 | 
			
		||||
        mszAppDescription = mAPPInfo.getAppDescription();
 | 
			
		||||
        mnAppIcon = mAPPInfo.getAppIcon();
 | 
			
		||||
 | 
			
		||||
        mszWinBoLLServerHost = GlobalApplication.isDebuging() ?  "https://dev.winboll.cc": "https://www.winboll.cc";
 | 
			
		||||
        mszWinBoLLServerHost = GlobalApplication.isDebuging() ?  "https://yun-preivew.winboll.cc": "https://yun.winboll.cc";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            mszAppVersionName = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Jun 05 08:59:45 GMT 2025
 | 
			
		||||
stageCount=8
 | 
			
		||||
#Thu Jul 17 11:39:14 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=libappbase
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.7
 | 
			
		||||
buildCount=14
 | 
			
		||||
baseBetaVersion=15.8.8
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.2
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Fri Jan 10 22:03:57 GMT 2025
 | 
			
		||||
#Tue Jun 24 11:17:30 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
libraryProject=libjc
 | 
			
		||||
baseVersion=1.0
 | 
			
		||||
publishVersion=1.0.0
 | 
			
		||||
buildCount=133
 | 
			
		||||
buildCount=135
 | 
			
		||||
baseBetaVersion=1.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ public class Main {
 | 
			
		||||
    public final static int JAR_RUNNING_MODE_JCNDK_DEBUG = 4;
 | 
			
		||||
    public final static int JAR_RUNNING_MODE_JC = 5;
 | 
			
		||||
    public final static int JAR_RUNNING_MODE_JC_DEBUG = 6;
 | 
			
		||||
    public enum JAR_RUNNING_MODE {
 | 
			
		||||
    public static enum JAR_RUNNING_MODE {
 | 
			
		||||
        UNKNOWN(JAR_RUNNING_MODE_UNKNOWN),
 | 
			
		||||
        CONSOLE(JAR_RUNNING_MODE_CONSOLE),
 | 
			
		||||
        CONSOLE_DEBUG(JAR_RUNNING_MODE_CONSOLE_DEBUG),
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.2" 
 | 
			
		||||
        versionName "15.3" 
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
@@ -45,9 +45,9 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.8.0'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.1'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.1'
 | 
			
		||||
    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 'io.github.medyo:android-about-page:2.0.0'
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue May 20 20:39:06 HKT 2025
 | 
			
		||||
stageCount=6
 | 
			
		||||
#Thu Jul 03 13:50:15 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.2
 | 
			
		||||
publishVersion=15.2.5
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.2.6
 | 
			
		||||
baseBetaVersion=15.3.2
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.App;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
 | 
			
		||||
public class AboutActivity extends WinBollActivity implements IWinBoLLActivity {
 | 
			
		||||
public class AboutActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "AboutActivity";
 | 
			
		||||
 | 
			
		||||
@@ -79,11 +79,11 @@ public class AboutActivity extends WinBollActivity implements IWinBoLLActivity {
 | 
			
		||||
        appInfo.setAppName(getString(R.string.app_name));
 | 
			
		||||
        appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
 | 
			
		||||
        appInfo.setAppDescription(getString(R.string.app_description));
 | 
			
		||||
        appInfo.setAppGitName("APP");
 | 
			
		||||
        appInfo.setAppGitName("APPBase");
 | 
			
		||||
        appInfo.setAppGitOwner("Studio");
 | 
			
		||||
        appInfo.setAppGitAPPBranch(szBranchName);
 | 
			
		||||
        appInfo.setAppGitAPPSubProjectFolder(szBranchName);
 | 
			
		||||
        appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=MyMessageManager");
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=5&extra=page%3D1");
 | 
			
		||||
        appInfo.setAppAPKName("MyMessageManager");
 | 
			
		||||
        appInfo.setAppAPKFolderName("MyMessageManager");
 | 
			
		||||
        return new AboutView(mContext, appInfo);
 | 
			
		||||
 
 | 
			
		||||
@@ -103,6 +103,9 @@ abstract public class BaseActivity extends AppCompatActivity {
 | 
			
		||||
        } else if (R.id.item_defaulttheme == item.getItemId()) {
 | 
			
		||||
            AESThemeUtil.saveThemeStyleID(this, R.style.MyAppTheme);
 | 
			
		||||
            recreate();
 | 
			
		||||
        } else if (R.id.item_defaulttheme == item.getItemId()) {
 | 
			
		||||
            AESThemeUtil.saveThemeStyleID(this, R.style.MyAppTheme);
 | 
			
		||||
            recreate();
 | 
			
		||||
        }
 | 
			
		||||
        //ToastUtils.show("nThemeStyleID " + Integer.toString(nThemeStyleID));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -128,7 +128,7 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
        mToolbar = findViewById(R.id.activitymainASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_main));
 | 
			
		||||
        setSupportActionBar(mToolbar);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        boolean isEnableService = mAppConfigUtil.mAppConfigBean.isEnableService();
 | 
			
		||||
        msvEnableService = findViewById(R.id.activitymainSwitchView1);
 | 
			
		||||
        msvEnableService.setChecked(isEnableService);
 | 
			
		||||
@@ -269,17 +269,9 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
    public boolean onCreateOptionsMenu(Menu menu) {
 | 
			
		||||
        //return super.onCreateOptionsMenu(menu);
 | 
			
		||||
        getMenuInflater().inflate(R.menu.toolbar_main, menu);
 | 
			
		||||
 | 
			
		||||
        /*ThemeUtil.BaseTheme baseTheme = ThemeUtil.getTheme(mAppConfigUtil.mAppConfigBean.getAppThemeID());
 | 
			
		||||
        if (baseTheme == ThemeUtil.BaseTheme.DEFAULT) {
 | 
			
		||||
            menu.findItem(R.id.app_defaulttheme).setChecked(true);
 | 
			
		||||
        } else if (baseTheme == ThemeUtil.BaseTheme.SKY) {
 | 
			
		||||
            menu.findItem(R.id.app_skytheme).setChecked(true);
 | 
			
		||||
        } else if (baseTheme == ThemeUtil.BaseTheme.GOLDEN) {
 | 
			
		||||
            menu.findItem(R.id.app_goldentheme).setChecked(true);
 | 
			
		||||
        }*/
 | 
			
		||||
 | 
			
		||||
        return super.onCreateOptionsMenu(menu);
 | 
			
		||||
        super.onCreateOptionsMenu(menu);
 | 
			
		||||
		getMenuInflater().inflate(R.menu.toolbar_main2, menu);
 | 
			
		||||
		return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void reloadSMS() {
 | 
			
		||||
@@ -306,7 +298,7 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            startActivity(i);
 | 
			
		||||
		} else if (nItemId ==  R.id.app_log) {
 | 
			
		||||
            //App.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
            App.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
        } else if (nItemId ==  R.id.app_unittest) {
 | 
			
		||||
            Intent i = new Intent(MainActivity.this, UnitTestActivity.class);
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
@@ -324,7 +316,7 @@ public class MainActivity extends BaseActivity {
 | 
			
		||||
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
            startActivity(i);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,9 @@ import cc.winboll.studio.libaes.beans.AESThemeBean;
 | 
			
		||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
 | 
			
		||||
public class WinBollActivity extends AppCompatActivity implements IWinBoLLActivity {
 | 
			
		||||
public class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "WinBollActivity";
 | 
			
		||||
    public static final String TAG = "WinBoLLActivity";
 | 
			
		||||
 | 
			
		||||
    protected volatile AESThemeBean.ThemeType mThemeType;
 | 
			
		||||
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
 | 
			
		||||
	</ScrollView>
 | 
			
		||||
 | 
			
		||||
	<cc.winboll.studio.shared.log.LogView
 | 
			
		||||
	<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -17,26 +17,4 @@
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/app_smsrule"
 | 
			
		||||
        android:title="@string/text_smsrule"/>
 | 
			
		||||
    <item android:title="@string/app_developoptions">
 | 
			
		||||
        <menu>
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/app_log"
 | 
			
		||||
                android:title="@string/app_log"/>
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/app_unittest"
 | 
			
		||||
                android:title="@string/app_unittest"/>
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/app_crashtest"
 | 
			
		||||
                android:title="@string/app_crashtest"/>
 | 
			
		||||
        </menu>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/app_appsettings"
 | 
			
		||||
        android:title="@string/text_appsettings"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/app_about"
 | 
			
		||||
        android:title="@string/app_about"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/app_smsrecycle"
 | 
			
		||||
        android:title="@string/app_smsrecycle"/>
 | 
			
		||||
</menu>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								mymessagemanager/src/main/res/menu/toolbar_main2.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								mymessagemanager/src/main/res/menu/toolbar_main2.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<?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:title="@string/app_developoptions">
 | 
			
		||||
        <menu>
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/app_log"
 | 
			
		||||
                android:title="@string/app_log"/>
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/app_unittest"
 | 
			
		||||
                android:title="@string/app_unittest"/>
 | 
			
		||||
            <item
 | 
			
		||||
                android:id="@+id/app_crashtest"
 | 
			
		||||
                android:title="@string/app_crashtest"/>
 | 
			
		||||
        </menu>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/app_appsettings"
 | 
			
		||||
        android:title="@string/text_appsettings"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/app_about"
 | 
			
		||||
        android:title="@string/app_about"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/app_smsrecycle"
 | 
			
		||||
        android:title="@string/app_smsrecycle"/>
 | 
			
		||||
</menu>
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
 | 
			
		||||
// JC 项目编译设置
 | 
			
		||||
//include ':jc'
 | 
			
		||||
//include ':libjc'
 | 
			
		||||
//rootProject.name = "jc"
 | 
			
		||||
 | 
			
		||||
// AES 项目编译设置
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user