Compare commits
	
		
			126 Commits
		
	
	
		
			7e263447c8
			...
			348edc8aaf
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					348edc8aaf | ||
| 
						 | 
					5970ae33c8 | ||
| 
						 | 
					91a44f48ef | ||
| 
						 | 
					c66e9a090b | ||
| 
						 | 
					7a14b55247 | ||
| 
						 | 
					fab68f16c8 | ||
| 
						 | 
					3a97c6135f | ||
| 
						 | 
					cdad017d8c | ||
| 
						 | 
					bf84382963 | ||
| 
						 | 
					34601fc5b1 | ||
| 
						 | 
					ea65810e7d | ||
| 
						 | 
					ad991e3da2 | ||
| 
						 | 
					80201e8370 | ||
| 
						 | 
					ea0473606a | ||
| 
						 | 
					870e9a94fb | ||
| 
						 | 
					2421ecb943 | ||
| 
						 | 
					687fff7216 | ||
| 
						 | 
					50d4cd830b | ||
| 
						 | 
					2079822c00 | ||
| 
						 | 
					297c76f328 | ||
| 
						 | 
					43b18ee662 | ||
| 
						 | 
					3ec3a4cfc2 | ||
| 
						 | 
					2c10a9f38c | ||
| 
						 | 
					e62888636e | ||
| 
						 | 
					364980dd02 | ||
| 
						 | 
					0e155e4f3a | ||
| 
						 | 
					89febba5a9 | ||
| 
						 | 
					653330f8e1 | ||
| 
						 | 
					386c73effc | ||
| 
						 | 
					b385583c5a | ||
| 
						 | 
					e7a9be2f56 | ||
| 
						 | 
					b27f7b0080 | ||
| 
						 | 
					0a440419ff | ||
| 
						 | 
					9e189ed5ac | ||
| 
						 | 
					0fb6aadc72 | ||
| 
						 | 
					46f3315b02 | ||
| 
						 | 
					c0ff228845 | ||
| 
						 | 
					a0fe8f17a8 | ||
| 
						 | 
					16bd40fc59 | ||
| 
						 | 
					ffaf683c54 | ||
| 
						 | 
					26f5f8d3db | ||
| 
						 | 
					917e25cdc8 | ||
| 
						 | 
					3795cf8631 | ||
| 
						 | 
					b374f3117a | ||
| 
						 | 
					d581cd9842 | ||
| 
						 | 
					cef50d087d | ||
| 
						 | 
					6d9adc124e | ||
| 
						 | 
					52f738b45b | ||
| 
						 | 
					9ece6778b7 | ||
| 
						 | 
					b7f8b76ace | ||
| 
						 | 
					326e5fa68e | ||
| 
						 | 
					e9c8f9029e | ||
| 
						 | 
					c00bfa1292 | ||
| 
						 | 
					11ee4dcf27 | ||
| 
						 | 
					8974e24dce | ||
| 
						 | 
					89142e379c | ||
| 
						 | 
					dabc671c27 | ||
| 
						 | 
					ed849e92d1 | ||
| 
						 | 
					42d2522927 | ||
| 
						 | 
					6a52b2a8c3 | ||
| 
						 | 
					02ed5bd5d1 | ||
| 
						 | 
					b685665d0c | ||
| 
						 | 
					1b030a2855 | ||
| 
						 | 
					dd577f1765 | ||
| 
						 | 
					0f8350600d | ||
| 
						 | 
					1777ebb8dc | ||
| 
						 | 
					2b99f707e7 | ||
| 
						 | 
					068c94e749 | ||
| 
						 | 
					0b3bc7e296 | ||
| 
						 | 
					6ef747bcf8 | ||
| 
						 | 
					a8e843c388 | ||
| 
						 | 
					d3fd593cb0 | ||
| 
						 | 
					3aec176b8b | ||
| 
						 | 
					5960f76238 | ||
| 
						 | 
					1a118da827 | ||
| 
						 | 
					f73cad6f3e | ||
| 
						 | 
					7130ecf023 | ||
| 
						 | 
					953c8f08cd | ||
| 
						 | 
					87b7557f72 | ||
| 
						 | 
					77f5a156f8 | ||
| 
						 | 
					b34ea40536 | ||
| 
						 | 
					d202a3443d | ||
| 
						 | 
					8c532c885f | ||
| 
						 | 
					5fc4cb5f74 | ||
| 
						 | 
					2a590a99fb | ||
| 
						 | 
					c6ad707ca2 | ||
| 
						 | 
					ee13a43fb6 | ||
| 
						 | 
					5fbe1d8f71 | ||
| 
						 | 
					e13c8e7af0 | ||
| 
						 | 
					a4988b5b68 | ||
| 
						 | 
					04df902b6b | ||
| 
						 | 
					33c71ea868 | ||
| 
						 | 
					5507126f6b | ||
| 
						 | 
					d381c29452 | ||
| 
						 | 
					ba861d910e | ||
| 
						 | 
					f5d9aafe43 | ||
| 
						 | 
					eb61eb7306 | ||
| 
						 | 
					31ad66685c | ||
| 
						 | 
					beb561ad6a | ||
| 
						 | 
					8869265d60 | ||
| 
						 | 
					2739627aff | ||
| 
						 | 
					58e0be9cf4 | ||
| 
						 | 
					9e9402f84e | ||
| 
						 | 
					ec18330022 | ||
| 
						 | 
					8bb80ef575 | ||
| 
						 | 
					c1e6e32809 | ||
| 
						 | 
					3e7722e2c0 | ||
| 
						 | 
					a1707e73b5 | ||
| 
						 | 
					9dcbaa0d75 | ||
| 
						 | 
					07b5e66875 | ||
| 
						 | 
					91f5cf9051 | ||
| 
						 | 
					8730f434dd | ||
| 
						 | 
					f8944490f8 | ||
| 
						 | 
					733af004f6 | ||
| 
						 | 
					c03568e1f5 | ||
| 
						 | 
					a0575a5e8b | ||
| 
						 | 
					0e57ce679e | ||
| 
						 | 
					f9211a8eb4 | ||
| 
						 | 
					4c31ff9b54 | ||
| 
						 | 
					8cf610962e | ||
| 
						 | 
					3071d186ec | ||
| 
						 | 
					df10306059 | ||
| 
						 | 
					ccdb9c5abd | ||
| 
						 | 
					f27209ab87 | ||
| 
						 | 
					2a819e94e4 | ||
| 
						 | 
					6635358ec5 | 
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sat Jun 28 12:59:51 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
#Sun Aug 31 23:40:17 HKT 2025
 | 
			
		||||
stageCount=4
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
publishVersion=15.9.3
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
baseBetaVersion=15.9.4
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,6 @@ dependencies {
 | 
			
		||||
    // https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
 | 
			
		||||
    api 'com.android.support:recyclerview-v7:28.0.0'
 | 
			
		||||
    
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.5'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sun Jun 01 08:02:46 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
#Sun Aug 31 05:11:26 CST 2025
 | 
			
		||||
stageCount=1
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.0
 | 
			
		||||
publishVersion=15.0.0
 | 
			
		||||
buildCount=27
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.0.1
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ android {
 | 
			
		||||
        // versionName 更新后需要手动设置 
 | 
			
		||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
			
		||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
			
		||||
        versionName "15.0"
 | 
			
		||||
        versionName "15.1"
 | 
			
		||||
        if(true) {
 | 
			
		||||
            versionName = genVersionName("${versionName}")
 | 
			
		||||
        }
 | 
			
		||||
@@ -67,7 +67,7 @@ dependencies {
 | 
			
		||||
    //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
 | 
			
		||||
    //api 'androidx.fragment:fragment:1.1.0'
 | 
			
		||||
    
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.3'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.5'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sat Jun 28 05:02:54 GMT 2025
 | 
			
		||||
stageCount=0
 | 
			
		||||
#Sun Aug 31 05:42:50 CST 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.0
 | 
			
		||||
publishVersion=15.0.0
 | 
			
		||||
buildCount=27
 | 
			
		||||
baseBetaVersion=15.0.1
 | 
			
		||||
baseVersion=15.1
 | 
			
		||||
publishVersion=15.1.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.1.2
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue Jun 03 15:05:48 HKT 2025
 | 
			
		||||
stageCount=5
 | 
			
		||||
#Sun Aug 31 23:39:16 HKT 2025
 | 
			
		||||
stageCount=6
 | 
			
		||||
libraryProject=libapputils
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.4
 | 
			
		||||
publishVersion=15.8.5
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.5
 | 
			
		||||
baseBetaVersion=15.8.6
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								autoinstaller/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								autoinstaller/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
# AutoInstaller
 | 
			
		||||
 | 
			
		||||
#### 介绍
 | 
			
		||||
APK 文件监控,监控指定文件路径的 APK 文件。
 | 
			
		||||
在 APK 文件改变后自动调用系统安装命令安装,或者调用[应用信息查看器]打开。
 | 
			
		||||
 | 
			
		||||
#### 软件架构
 | 
			
		||||
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
 | 
			
		||||
也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Gradle 编译说明
 | 
			
		||||
调试版编译命令 :gradle assembleBetaDebug
 | 
			
		||||
阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh autoinstaller
 | 
			
		||||
 | 
			
		||||
#### 使用说明
 | 
			
		||||
 | 
			
		||||
#### 参与贡献
 | 
			
		||||
 | 
			
		||||
1.  Fork 本仓库
 | 
			
		||||
2.  新建 Feat_xxx 分支
 | 
			
		||||
3.  提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>)
 | 
			
		||||
4.  新建 Pull Request
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### 特技
 | 
			
		||||
 | 
			
		||||
1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
 | 
			
		||||
2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
 | 
			
		||||
3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
 | 
			
		||||
4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
 | 
			
		||||
5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
 | 
			
		||||
6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
 | 
			
		||||
 | 
			
		||||
#### 参考文档
 | 
			
		||||
@@ -45,9 +45,9 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.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
 | 
			
		||||
#Thu Jul 17 09:57:24 HKT 2025
 | 
			
		||||
stageCount=12
 | 
			
		||||
#Sun Aug 31 06:05:42 CST 2025
 | 
			
		||||
stageCount=17
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.11
 | 
			
		||||
publishVersion=15.3.16
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.12
 | 
			
		||||
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();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -154,6 +155,62 @@ 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(); // 替换为实际的目标文件夹路径
 | 
			
		||||
@@ -170,9 +227,9 @@ public class TomCat {
 | 
			
		||||
 | 
			
		||||
    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()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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()));
 | 
			
		||||
        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;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,6 @@
 | 
			
		||||
<resources>
 | 
			
		||||
 | 
			
		||||
    <string name="app_name">Contacts</string>
 | 
			
		||||
    <string name="default_bobulltoon_url">https://gitea.winboll.cc/Studio/BoBullToon/archive/main.zip</string>
 | 
			
		||||
    <string name="default_bobulltoon_url">https://gitee.com/zhangsken/bobulltoon/repository/archive/main.zip</string>
 | 
			
		||||
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.5'
 | 
			
		||||
	api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
    
 | 
			
		||||
    // 吐司类库
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sat Jun 28 12:59:30 HKT 2025
 | 
			
		||||
stageCount=3
 | 
			
		||||
#Sun Aug 31 05:00:43 CST 2025
 | 
			
		||||
stageCount=4
 | 
			
		||||
libraryProject=libaes
 | 
			
		||||
baseVersion=15.9
 | 
			
		||||
publishVersion=15.9.2
 | 
			
		||||
publishVersion=15.9.3
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.9.3
 | 
			
		||||
baseBetaVersion=15.9.4
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.2'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
    
 | 
			
		||||
    // 二维码类库
 | 
			
		||||
    api 'com.google.zxing:core:3.4.1'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Tue Jun 03 15:05:42 HKT 2025
 | 
			
		||||
stageCount=5
 | 
			
		||||
#Sun Aug 31 04:53:04 CST 2025
 | 
			
		||||
stageCount=6
 | 
			
		||||
libraryProject=libapputils
 | 
			
		||||
baseVersion=15.8
 | 
			
		||||
publishVersion=15.8.4
 | 
			
		||||
publishVersion=15.8.5
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.8.5
 | 
			
		||||
baseBetaVersion=15.8.6
 | 
			
		||||
 
 | 
			
		||||
@@ -45,15 +45,17 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.2'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.8.4'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.3'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.5'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
    
 | 
			
		||||
	api 'io.github.medyo:android-about-page:2.0.0'
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
    api 'com.jcraft:jsch:0.1.55'
 | 
			
		||||
    api 'org.jsoup:jsoup:1.13.1'
 | 
			
		||||
    api 'com.squareup.okhttp3:okhttp:4.4.1'
 | 
			
		||||
	
 | 
			
		||||
	api 'com.belerweb:pinyin4j:2.5.1'
 | 
			
		||||
    
 | 
			
		||||
    // 权限请求框架:https://github.com/getActivity/XXPermissions
 | 
			
		||||
    api 'com.github.getActivity:XXPermissions:18.63'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu Jul 03 13:50:15 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Sun Aug 31 06:13:45 CST 2025
 | 
			
		||||
stageCount=7
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.3
 | 
			
		||||
publishVersion=15.3.1
 | 
			
		||||
publishVersion=15.3.6
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.3.2
 | 
			
		||||
baseBetaVersion=15.3.7
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.activitys;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 联系人查询与短信发送窗口
 | 
			
		||||
 */
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.text.Editable;
 | 
			
		||||
import android.text.TextWatcher;
 | 
			
		||||
@@ -11,13 +16,17 @@ import android.widget.RelativeLayout;
 | 
			
		||||
import android.widget.SimpleAdapter;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toolbar;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.graphics.drawable.Drawable;
 | 
			
		||||
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.ComposeSMSActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.PhoneBean;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.PhoneUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -26,166 +35,282 @@ import java.util.Map;
 | 
			
		||||
public class ComposeSMSActivity extends BaseActivity {
 | 
			
		||||
 | 
			
		||||
    public static String TAG = "ComposeSMSActivity";
 | 
			
		||||
 | 
			
		||||
    public static String EXTRA_SMSBODY = "sms_body";
 | 
			
		||||
    private static final String MAP_NAME = "NAME";
 | 
			
		||||
    private static final String MAP_PHONE = "PHONE";
 | 
			
		||||
 | 
			
		||||
    static String MAP_NAME = "NAME";
 | 
			
		||||
    static String MAP_PHONE = "PHONE";
 | 
			
		||||
 | 
			
		||||
    String mszSMSBody;
 | 
			
		||||
    String mszScheme;
 | 
			
		||||
    String mszPhoneTo;
 | 
			
		||||
    EditText metTO;
 | 
			
		||||
    EditText metSMSBody;
 | 
			
		||||
    SimpleAdapter mSimpleAdapter;
 | 
			
		||||
    List<Map<String,Object>> mAdapterData = new ArrayList<>();
 | 
			
		||||
    ListView mlvContracts;
 | 
			
		||||
    List<PhoneBean> mListPhoneBeanContracts;
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
    AOHPCTCSeekBar mAOHPCTCSeekBar;
 | 
			
		||||
    RelativeLayout mrlContracts;
 | 
			
		||||
    private String mszSMSBody;
 | 
			
		||||
    private String mszScheme;
 | 
			
		||||
    private String mszPhoneTo;
 | 
			
		||||
    private TextView mtvTOName;
 | 
			
		||||
    private EditText metTONameSearch;
 | 
			
		||||
    private EditText metTO;
 | 
			
		||||
    private EditText metSMSBody;
 | 
			
		||||
    private SimpleAdapter mSimpleAdapter;
 | 
			
		||||
    private List<Map<String, Object>> mAdapterData = new ArrayList<Map<String, Object>>();
 | 
			
		||||
    private ListView mlvContracts;
 | 
			
		||||
    private List<PhoneBean> mListPhoneBeanContracts;
 | 
			
		||||
    private Toolbar mToolbar;
 | 
			
		||||
    private AOHPCTCSeekBar mAOHPCTCSeekBar;
 | 
			
		||||
    private RelativeLayout mrlContracts;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_composesms);
 | 
			
		||||
        mszSMSBody = getIntent().getStringExtra(EXTRA_SMSBODY);
 | 
			
		||||
        mszScheme = getIntent().getData().getScheme();
 | 
			
		||||
        mszPhoneTo = getIntent().getData().getSchemeSpecificPart();
 | 
			
		||||
        if (!mszScheme.equals("smsto")) {
 | 
			
		||||
            // 其他方式未支持就退出
 | 
			
		||||
            finish();
 | 
			
		||||
 | 
			
		||||
        // 初始化Intent数据(增加空判断,避免NullPointerException)
 | 
			
		||||
        Intent intent = getIntent();
 | 
			
		||||
        if (intent != null) {
 | 
			
		||||
            mszSMSBody = intent.getStringExtra(EXTRA_SMSBODY);
 | 
			
		||||
            if (intent.getData() != null) {
 | 
			
		||||
                mszScheme = intent.getData().getScheme();
 | 
			
		||||
                mszPhoneTo = intent.getData().getSchemeSpecificPart();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 初始化视图
 | 
			
		||||
 | 
			
		||||
        // 校验启动方式,非smsto则退出
 | 
			
		||||
        if (mszScheme == null || !"smsto".equals(mszScheme)) {
 | 
			
		||||
            ToastUtils.show("不支持的启动方式");
 | 
			
		||||
            finish();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        initView();
 | 
			
		||||
        // 设置适配器
 | 
			
		||||
        initAdapter();
 | 
			
		||||
        // 设置搜索到的匹配位置
 | 
			
		||||
        setListViewPrePosition();
 | 
			
		||||
        initAdapter(null); // 初始加载所有联系人
 | 
			
		||||
        setListViewPrePositionByPhone();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 初始化视图
 | 
			
		||||
    //
 | 
			
		||||
    void initView() {
 | 
			
		||||
        //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame);
 | 
			
		||||
 | 
			
		||||
    private void initView() {
 | 
			
		||||
        // 初始化标题栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitycomposesmsASupportToolbar1);
 | 
			
		||||
        mToolbar = (Toolbar) findViewById(R.id.activitycomposesmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_composesms));
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人栏目框
 | 
			
		||||
        mrlContracts = findViewById(R.id.activitycomposesmsRelativeLayout1);
 | 
			
		||||
        //mrlContracts.setBackground(drawableFrame);
 | 
			
		||||
        // 初始化联系人姓名显示和搜索栏
 | 
			
		||||
        mtvTOName = (TextView) findViewById(R.id.activitycomposesmsTextView2);
 | 
			
		||||
        mrlContracts = (RelativeLayout) findViewById(R.id.activitycomposesmsRelativeLayout1);
 | 
			
		||||
        metTONameSearch = (EditText) findViewById(R.id.activitycomposesmsEditText2);
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人列表
 | 
			
		||||
        mlvContracts = findViewById(R.id.activitycomposesmsListView1);
 | 
			
		||||
        // 姓名搜索框文本变化监听
 | 
			
		||||
        metTONameSearch.addTextChangedListener(new TextWatcher() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
					metTO.setText(""); // 清空号码输入框,避免冲突
 | 
			
		||||
					String input = s == null ? "" : s.toString().trim();
 | 
			
		||||
					if (input.isEmpty()) {
 | 
			
		||||
						initAdapter(null); // 空搜索时显示所有联系人
 | 
			
		||||
					} else {
 | 
			
		||||
						setListViewPrePositionByName(); // 按姓名搜索
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
        // 初始化联系人输入框
 | 
			
		||||
        metTO = findViewById(R.id.activitycomposesmsEditText1);
 | 
			
		||||
        metTO.setText(mszPhoneTo);
 | 
			
		||||
        metTO.addTextChangedListener(new TextWatcher() {           
 | 
			
		||||
                @Override  
 | 
			
		||||
                public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
                    setListViewPrePosition();
 | 
			
		||||
                }
 | 
			
		||||
                @Override  
 | 
			
		||||
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                @Override  
 | 
			
		||||
                public void afterTextChanged(Editable s) {
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // 初始化发送拉动控件
 | 
			
		||||
        mAOHPCTCSeekBar = findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        mAOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message));
 | 
			
		||||
        mAOHPCTCSeekBar.setThumbOffset(20);
 | 
			
		||||
        mAOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onOHPCommit() {
 | 
			
		||||
                    // 空号码不发送
 | 
			
		||||
                    mszPhoneTo = metTO.getText().toString();
 | 
			
		||||
                    if (mszPhoneTo.trim().equals("")) {
 | 
			
		||||
                        ToastUtils.show("没有设置接收号码。");
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    // 空消息不发送
 | 
			
		||||
                    mszSMSBody = metSMSBody.getText().toString();
 | 
			
		||||
                    if (mszSMSBody.equals("")) {
 | 
			
		||||
                        ToastUtils.show("没有消息内容可发送。");
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    // 发送消息
 | 
			
		||||
                    if (SMSUtil.sendMessageByInterface2(ComposeSMSActivity.this, mszPhoneTo, mszSMSBody)) {
 | 
			
		||||
                        ComposeSMSActivity.this.finish();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void afterTextChanged(Editable s) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化提示框
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        // 初始化联系人列表
 | 
			
		||||
        mlvContracts = (ListView) findViewById(R.id.activitycomposesmsListView1);
 | 
			
		||||
 | 
			
		||||
        // 初始化号码输入框(核心:优化文本变化监听逻辑)
 | 
			
		||||
        metTO = (EditText) findViewById(R.id.activitycomposesmsEditText1);
 | 
			
		||||
        if (mszPhoneTo != null) {
 | 
			
		||||
            metTO.setText(mszPhoneTo);
 | 
			
		||||
        }
 | 
			
		||||
        metTO.addTextChangedListener(new TextWatcher() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
			
		||||
					mtvTOName.setText(""); // 清空姓名显示
 | 
			
		||||
					String inputPhone = s == null ? "" : s.toString().trim();
 | 
			
		||||
 | 
			
		||||
					if (inputPhone.isEmpty()) {
 | 
			
		||||
						// 输入为空时,显示所有联系人
 | 
			
		||||
						initAdapter(null);
 | 
			
		||||
					} else {
 | 
			
		||||
						// 输入非空时,按号码搜索并更新列表(无结果则清空)
 | 
			
		||||
						filterListByPhone(inputPhone);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				@Override
 | 
			
		||||
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				@Override
 | 
			
		||||
				public void afterTextChanged(Editable s) {
 | 
			
		||||
					// 无操作
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化发送控件
 | 
			
		||||
        mAOHPCTCSeekBar = (AOHPCTCSeekBar) findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        Drawable thumbDrawable = getResources().getDrawable(R.drawable.ic_message); // Java 7兼容写法
 | 
			
		||||
        mAOHPCTCSeekBar.setThumb(thumbDrawable);
 | 
			
		||||
        mAOHPCTCSeekBar.setThumbOffset(20);
 | 
			
		||||
        mAOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onOHPCommit() {
 | 
			
		||||
					sendSMS();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化短信内容输入框
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = (TextView) findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg);
 | 
			
		||||
 | 
			
		||||
        // 初始化发送消息框
 | 
			
		||||
        metSMSBody = findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        //metSMSBody.setBackground(drawableFrame);
 | 
			
		||||
        metSMSBody.setText(mszSMSBody);
 | 
			
		||||
        metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        if (mszSMSBody != null) {
 | 
			
		||||
            metSMSBody.setText(mszSMSBody);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 设置搜索到的匹配位置
 | 
			
		||||
    //
 | 
			
		||||
    void setListViewPrePosition() {
 | 
			
		||||
        int nPrePosition = getContractsDataPrePosition(metTO.getText().toString());
 | 
			
		||||
        mlvContracts.setSelected(false);
 | 
			
		||||
        mlvContracts.setSelection(nPrePosition);
 | 
			
		||||
    // 核心优化:根据输入号码筛选列表(无结果则显示空列表)
 | 
			
		||||
    private void filterListByPhone(String inputPhone) {
 | 
			
		||||
        PhoneUtil phoneUtil = new PhoneUtil(this);
 | 
			
		||||
        List<PhoneBean> allContacts = phoneUtil.getPhoneList();
 | 
			
		||||
        List<PhoneBean> matchedContacts = new ArrayList<PhoneBean>();
 | 
			
		||||
 | 
			
		||||
        // 遍历所有联系人,匹配包含输入号码的联系人
 | 
			
		||||
        for (PhoneBean contact : allContacts) {
 | 
			
		||||
            if (contact.getTelPhone().contains(inputPhone) 
 | 
			
		||||
                || phoneUtil.isTheSamePhoneNumber(contact.getTelPhone(), inputPhone)) {
 | 
			
		||||
                matchedContacts.add(contact);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LogUtils.d(TAG, "号码搜索:输入'" + inputPhone + "', 匹配" + matchedContacts.size() + "个结果");
 | 
			
		||||
 | 
			
		||||
        // 用筛选结果更新列表(无结果则传入空列表)
 | 
			
		||||
        initAdapter(matchedContacts.isEmpty() ? new ArrayList<PhoneBean>() : matchedContacts);
 | 
			
		||||
 | 
			
		||||
        // 定位到第一个匹配项(如果有)
 | 
			
		||||
        if (!matchedContacts.isEmpty()) {
 | 
			
		||||
            mlvContracts.setSelection(0);
 | 
			
		||||
            mtvTOName.setText(matchedContacts.get(0).getName());
 | 
			
		||||
        } else {
 | 
			
		||||
            mtvTOName.setText(""); // 无结果时清空姓名显示
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 返回搜索到的匹配位置
 | 
			
		||||
    //
 | 
			
		||||
    int getContractsDataPrePosition(String szPhone) {
 | 
			
		||||
    // 根据姓名搜索联系人
 | 
			
		||||
    private void setListViewPrePositionByName() {
 | 
			
		||||
        String searchName = metTONameSearch.getText().toString().trim();
 | 
			
		||||
        PhoneUtil phoneUtil = new PhoneUtil(this);
 | 
			
		||||
        List<PhoneBean> matchedContacts = phoneUtil.getPhonesByName(searchName);
 | 
			
		||||
        initAdapter(matchedContacts);
 | 
			
		||||
        if (!matchedContacts.isEmpty()) {
 | 
			
		||||
            mlvContracts.setSelection(0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 初始定位号码对应的联系人
 | 
			
		||||
    private void setListViewPrePositionByPhone() {
 | 
			
		||||
        String inputPhone = metTO.getText().toString().trim();
 | 
			
		||||
        if (inputPhone.isEmpty()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        filterListByPhone(inputPhone); // 复用筛选逻辑
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 获取号码匹配的位置(兼容旧逻辑)
 | 
			
		||||
    private int getContractsDataPrePositionByPhone(String szPhone) {
 | 
			
		||||
        if (mListPhoneBeanContracts == null || mListPhoneBeanContracts.isEmpty()) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < mListPhoneBeanContracts.size(); i++) {
 | 
			
		||||
            if (mListPhoneBeanContracts.get(i).getTelPhone().compareTo(szPhone) > -1) {
 | 
			
		||||
            PhoneBean bean = mListPhoneBeanContracts.get(i);
 | 
			
		||||
            if (bean.getTelPhone().compareTo(szPhone) >= 0) {
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 初始化适配器
 | 
			
		||||
    //
 | 
			
		||||
    void initAdapter() {
 | 
			
		||||
        // 初始化联系人数据适配器
 | 
			
		||||
        mAdapterData = new ArrayList<>();
 | 
			
		||||
        // 读取联系人数据
 | 
			
		||||
        PhoneUtil phoneUtils = new PhoneUtil(this);
 | 
			
		||||
        mListPhoneBeanContracts = phoneUtils.getPhoneList();
 | 
			
		||||
        // 映射联系人数据给适配器数据对象
 | 
			
		||||
        for (int i = 0;i < mListPhoneBeanContracts.size();i++) {
 | 
			
		||||
            Map<String,Object> map =new HashMap<>();
 | 
			
		||||
            map.put(MAP_NAME, mListPhoneBeanContracts.get(i).getName());
 | 
			
		||||
            map.put(MAP_PHONE, mListPhoneBeanContracts.get(i).getTelPhone());
 | 
			
		||||
            mAdapterData.add(map);
 | 
			
		||||
    // 获取姓名匹配的位置(兼容旧逻辑)
 | 
			
		||||
    private int getContractsDataPrePositionByName(String szName) {
 | 
			
		||||
        if (mListPhoneBeanContracts == null || mListPhoneBeanContracts.isEmpty()) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        // 绑定适配器与数据
 | 
			
		||||
        mSimpleAdapter = new SimpleAdapter(ComposeSMSActivity.this, mAdapterData, R.layout.listview_contracts
 | 
			
		||||
                                           , new String[]{MAP_NAME, MAP_PHONE}
 | 
			
		||||
                                           , new int[]{R.id.listviewcontractsTextView1, R.id.listviewcontractsTextView2});
 | 
			
		||||
        mSimpleAdapter.setDropDownViewResource(R.layout.listview_contracts);
 | 
			
		||||
        mlvContracts.setAdapter(mSimpleAdapter);
 | 
			
		||||
        mlvContracts.setOnItemClickListener(new ListView.OnItemClickListener() {
 | 
			
		||||
        for (int i = 0; i < mListPhoneBeanContracts.size(); i++) {
 | 
			
		||||
            if (mListPhoneBeanContracts.get(i).getName().startsWith(szName)) {
 | 
			
		||||
                return i;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 | 
			
		||||
                    metTO.setText(mAdapterData.get(position).get(MAP_PHONE).toString());
 | 
			
		||||
    // 初始化或更新列表适配器
 | 
			
		||||
    private void initAdapter(List<PhoneBean> initData) {
 | 
			
		||||
        mAdapterData.clear(); // 清空旧数据
 | 
			
		||||
        final PhoneUtil phoneUtil = new PhoneUtil(this);
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        // 确定数据源:传入的筛选数据或所有联系人
 | 
			
		||||
        if (initData != null) {
 | 
			
		||||
            mListPhoneBeanContracts = initData;
 | 
			
		||||
        } else {
 | 
			
		||||
            mListPhoneBeanContracts = phoneUtil.getPhoneList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 转换数据为SimpleAdapter所需格式
 | 
			
		||||
        if (mListPhoneBeanContracts != null) {
 | 
			
		||||
            for (PhoneBean bean : mListPhoneBeanContracts) {
 | 
			
		||||
                Map<String, Object> map = new HashMap<String, Object>();
 | 
			
		||||
                map.put(MAP_NAME, bean.getName());
 | 
			
		||||
                map.put(MAP_PHONE, bean.getTelPhone());
 | 
			
		||||
                mAdapterData.add(map);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化或更新适配器
 | 
			
		||||
        if (mSimpleAdapter == null) {
 | 
			
		||||
            mSimpleAdapter = new SimpleAdapter(
 | 
			
		||||
                ComposeSMSActivity.this,
 | 
			
		||||
                mAdapterData,
 | 
			
		||||
                R.layout.listview_contracts,
 | 
			
		||||
                new String[]{MAP_NAME, MAP_PHONE},
 | 
			
		||||
                new int[]{R.id.listviewcontractsTextView1, R.id.listviewcontractsTextView2}
 | 
			
		||||
            );
 | 
			
		||||
            mSimpleAdapter.setDropDownViewResource(R.layout.listview_contracts);
 | 
			
		||||
            mlvContracts.setAdapter(mSimpleAdapter);
 | 
			
		||||
 | 
			
		||||
            // 列表项点击事件
 | 
			
		||||
            mlvContracts.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 | 
			
		||||
						if (position < mAdapterData.size()) {
 | 
			
		||||
							String phone = mAdapterData.get(position).get(MAP_PHONE).toString();
 | 
			
		||||
							metTO.setText(phone);
 | 
			
		||||
							mtvTOName.setText(phoneUtil.getNameByPhone(phone));
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
        } else {
 | 
			
		||||
            mSimpleAdapter.notifyDataSetChanged(); // 数据更新时通知适配器
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 发送短信逻辑
 | 
			
		||||
    private void sendSMS() {
 | 
			
		||||
        String phoneTo = metTO.getText().toString().trim();
 | 
			
		||||
        if (phoneTo.isEmpty()) {
 | 
			
		||||
            ToastUtils.show("没有设置接收号码。");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        String smsBody = metSMSBody.getText().toString().trim();
 | 
			
		||||
        if (smsBody.isEmpty()) {
 | 
			
		||||
            ToastUtils.show("没有消息内容可发送。");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (SMSUtil.sendMessageByInterface2(ComposeSMSActivity.this, phoneTo, smsBody)) {
 | 
			
		||||
            finish();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,16 @@ import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Message;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.view.inputmethod.InputMethodManager;
 | 
			
		||||
import android.widget.AbsListView;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
@@ -16,19 +21,17 @@ import android.widget.Toolbar;
 | 
			
		||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.R;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.activitys.SMSActivity;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.adapters.SMSArrayAdapter;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.AddressUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.SMSUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.utils.ViewUtil;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.BottomPositionFixedScrollView;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView;
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
 | 
			
		||||
public class SMSActivity extends BaseActivity {
 | 
			
		||||
    public static String TAG = "SMSActivity";
 | 
			
		||||
 | 
			
		||||
    public static final String ACTION_NOTIFY_SMS_CHANGED = "cc.winboll.studio.mymessagemanager.activitys.SMSActivity.ACTION_NOTIFY_SMS_CHANGED";
 | 
			
		||||
 | 
			
		||||
    public static final String EXTRA_PHONE = "Phone";
 | 
			
		||||
    final static int MSG_SET_FOCUS = 0;
 | 
			
		||||
 | 
			
		||||
@@ -36,10 +39,11 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
    Toolbar mToolbar;
 | 
			
		||||
    String mszPhoneTo;
 | 
			
		||||
    SMSArrayAdapter mSMSArrayAdapter;
 | 
			
		||||
    ScrollView mScrollView;
 | 
			
		||||
    BottomPositionFixedScrollView mScrollView1;
 | 
			
		||||
    EditText metSMSBody;
 | 
			
		||||
    SMSActivityBroadcastReceiver mSMSActivityBroadcastReceiver;
 | 
			
		||||
    Handler mSetFocusHandler;
 | 
			
		||||
    private boolean isImeVisible = false;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
@@ -47,45 +51,90 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
        setContentView(R.layout.activity_sms);
 | 
			
		||||
 | 
			
		||||
        initView();
 | 
			
		||||
		mSetFocusHandler = new MyHandler(SMSActivity.this);
 | 
			
		||||
        scrollScrollView();
 | 
			
		||||
        setupImeStatusListener();
 | 
			
		||||
 | 
			
		||||
        // 每隔一定时间设置输入框获得焦点
 | 
			
		||||
        //
 | 
			
		||||
        new Thread() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                while (true) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        Thread.sleep(1500);
 | 
			
		||||
                    } catch (InterruptedException e) {}
 | 
			
		||||
                    Message message = mSetFocusHandler.obtainMessage(MSG_SET_FOCUS);
 | 
			
		||||
                    mSetFocusHandler.sendMessage(message);
 | 
			
		||||
                }
 | 
			
		||||
            }}.start();
 | 
			
		||||
        // 新增:监听窗口加载完成,触发mScrollView1滚动到底部
 | 
			
		||||
        setupScrollToBottomAfterWindowLoaded();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 设置输入框获得焦点的类
 | 
			
		||||
	//
 | 
			
		||||
	static class MyHandler extends Handler {
 | 
			
		||||
		WeakReference<SMSActivity> mActivity;  
 | 
			
		||||
		MyHandler(SMSActivity activity) {  
 | 
			
		||||
			mActivity = new WeakReference<SMSActivity>(activity);  
 | 
			
		||||
		}
 | 
			
		||||
        public void handleMessage(Message msg) {
 | 
			
		||||
			SMSActivity theActivity = mActivity.get();
 | 
			
		||||
            switch (msg.what) {
 | 
			
		||||
                case MSG_SET_FOCUS:
 | 
			
		||||
                    theActivity.metSMSBody.setFocusable(true);
 | 
			
		||||
                    theActivity.metSMSBody.requestFocus();
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            super.handleMessage(msg);
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
    // 新增:窗口加载完成后让mScrollView1滚动到底部
 | 
			
		||||
    private void setupScrollToBottomAfterWindowLoaded() {
 | 
			
		||||
        final View rootView = findViewById(android.R.id.content);
 | 
			
		||||
        // 监听根布局绘制完成(窗口加载完成的标志)
 | 
			
		||||
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onGlobalLayout() {
 | 
			
		||||
					// 滚动到底部
 | 
			
		||||
					mScrollView1.post(new Runnable() {
 | 
			
		||||
							@Override
 | 
			
		||||
							public void run() {
 | 
			
		||||
								mScrollView1.fullScroll(ScrollView.FOCUS_DOWN);
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
 | 
			
		||||
					// 移除监听,避免重复触发
 | 
			
		||||
					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
 | 
			
		||||
						rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
 | 
			
		||||
					} else {
 | 
			
		||||
						rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupImeStatusListener() {
 | 
			
		||||
        final View rootView = findViewById(android.R.id.content);
 | 
			
		||||
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onGlobalLayout() {
 | 
			
		||||
					int rootViewHeight = rootView.getHeight();
 | 
			
		||||
					int screenHeight = getResources().getDisplayMetrics().heightPixels;
 | 
			
		||||
					int imeThreshold = dp2px(200);
 | 
			
		||||
 | 
			
		||||
					boolean currentImeVisible = (screenHeight - rootViewHeight) > imeThreshold;
 | 
			
		||||
 | 
			
		||||
					if (currentImeVisible != isImeVisible) {
 | 
			
		||||
						isImeVisible = currentImeVisible;
 | 
			
		||||
						setupScrollView1Height();
 | 
			
		||||
						if (!isImeVisible) {
 | 
			
		||||
							metSMSBody.clearFocus();
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
 | 
			
		||||
						rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
 | 
			
		||||
					} else {
 | 
			
		||||
						rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
 | 
			
		||||
					}
 | 
			
		||||
					setupImeStatusListener();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int dp2px(int dp) {
 | 
			
		||||
        return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*static class MyHandler extends Handler {
 | 
			
		||||
	 WeakReference<SMSActivity> mActivity;
 | 
			
		||||
	 MyHandler(SMSActivity activity) {
 | 
			
		||||
	 mActivity = new WeakReference<SMSActivity>(activity);
 | 
			
		||||
	 }
 | 
			
		||||
	 public void handleMessage(Message msg) {
 | 
			
		||||
	 SMSActivity theActivity = mActivity.get();
 | 
			
		||||
	 switch (msg.what) {
 | 
			
		||||
	 case MSG_SET_FOCUS:
 | 
			
		||||
	 theActivity.metSMSBody.setFocusable(true);
 | 
			
		||||
	 theActivity.metSMSBody.requestFocus();
 | 
			
		||||
	 theActivity.setupScrollView1Height();
 | 
			
		||||
	 break;
 | 
			
		||||
	 default:
 | 
			
		||||
	 break;
 | 
			
		||||
	 }
 | 
			
		||||
	 super.handleMessage(msg);
 | 
			
		||||
	 }
 | 
			
		||||
	 }*/
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
@@ -94,135 +143,130 @@ public class SMSActivity extends BaseActivity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void initView() {
 | 
			
		||||
        // 发送端空号码退出
 | 
			
		||||
        mszPhoneTo = getIntent().getStringExtra(EXTRA_PHONE);
 | 
			
		||||
        if (mszPhoneTo == null || mszPhoneTo.trim().equals("")) {
 | 
			
		||||
            finish();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化标题栏
 | 
			
		||||
        mToolbar = findViewById(R.id.activitysmsASupportToolbar1);
 | 
			
		||||
        mToolbar = (Toolbar) findViewById(R.id.activitysmsASupportToolbar1);
 | 
			
		||||
        mToolbar.setSubtitle(getString(R.string.activity_name_smsinphone) + " < Phone : " + AddressUtils.getFormattedAddress(mszPhoneTo) + " >");
 | 
			
		||||
        setActionBar(mToolbar);
 | 
			
		||||
 | 
			
		||||
        // 初始化滚动窗口
 | 
			
		||||
        mScrollView = findViewById(R.id.activitysmsinphoneScrollView1);
 | 
			
		||||
        mScrollView1 = (BottomPositionFixedScrollView) findViewById(R.id.activitysmsScrollView1);
 | 
			
		||||
 | 
			
		||||
        // 初始化发送消息框
 | 
			
		||||
        //Drawable drawableFrame = AppCompatResources.getDrawable(this, R.drawable.bg_frame);
 | 
			
		||||
        metSMSBody = findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        //metSMSBody.setBackground(drawableFrame);
 | 
			
		||||
        metSMSBody = (EditText) findViewById(R.id.viewsmssendpart1EditText1);
 | 
			
		||||
        metSMSBody.setOnClickListener(new View.OnClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onClick(View v) {
 | 
			
		||||
					setupScrollView1Height();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
        metSMSBody.setOnFocusChangeListener(new View.OnFocusChangeListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onFocusChange(View v, boolean hasFocus) {
 | 
			
		||||
					setupScrollView1Height();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化发送拉动控件
 | 
			
		||||
        final AOHPCTCSeekBar aOHPCTCSeekBar = findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        final AOHPCTCSeekBar aOHPCTCSeekBar = (AOHPCTCSeekBar) findViewById(R.id.viewsmssendpart1AOHPCTCSeekBar1);
 | 
			
		||||
        aOHPCTCSeekBar.setThumb(getDrawable(R.drawable.ic_message));
 | 
			
		||||
        aOHPCTCSeekBar.setThumbOffset(20);
 | 
			
		||||
        aOHPCTCSeekBar.setOnOHPCListener(
 | 
			
		||||
            new AOHPCTCSeekBar.OnOHPCListener(){
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onOHPCommit() {
 | 
			
		||||
                    //Toast.makeText(getApplication(), "Send", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    sendSMS();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        aOHPCTCSeekBar.setOnOHPCListener(new AOHPCTCSeekBar.OnOHPCListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onOHPCommit() {
 | 
			
		||||
					sendSMS();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        // 初始化提示框
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        TextView tvAOHPCTCSeekBarMSG = (TextView) findViewById(R.id.viewsmssendpart1TextView1);
 | 
			
		||||
        tvAOHPCTCSeekBarMSG.setText(R.string.msg_100sendmsg);
 | 
			
		||||
 | 
			
		||||
        mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsinphoneListView1);
 | 
			
		||||
 | 
			
		||||
        // 准备数据
 | 
			
		||||
        mlvSMS = (SMSListViewForScrollView) findViewById(R.id.activitysmsSMSListViewForScrollView1);
 | 
			
		||||
        mSMSArrayAdapter = new SMSArrayAdapter(SMSActivity.this, mszPhoneTo);
 | 
			
		||||
        mlvSMS.setAdapter(mSMSArrayAdapter);
 | 
			
		||||
 | 
			
		||||
        // 设置短信列表滚动到底部就取消已发送的通知消息
 | 
			
		||||
        //
 | 
			
		||||
        mlvSMS.setOnScrollListener(new AbsListView.OnScrollListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onScrollStateChanged(AbsListView view, int scrollState) {
 | 
			
		||||
                }
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onScrollStateChanged(AbsListView view, int scrollState) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 | 
			
		||||
                    if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
 | 
			
		||||
                        // 滑动到了底部
 | 
			
		||||
                        mSMSArrayAdapter.cancelMessageNotification();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 | 
			
		||||
					if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
 | 
			
		||||
						mSMSArrayAdapter.cancelMessageNotification();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        mSMSActivityBroadcastReceiver = new SMSActivityBroadcastReceiver();
 | 
			
		||||
        IntentFilter intentFilter = new IntentFilter();
 | 
			
		||||
        intentFilter.addAction(ACTION_NOTIFY_SMS_CHANGED);
 | 
			
		||||
        IntentFilter intentFilter = new IntentFilter(ACTION_NOTIFY_SMS_CHANGED);
 | 
			
		||||
        LocalBroadcastManager.getInstance(this).registerReceiver(mSMSActivityBroadcastReceiver, intentFilter);
 | 
			
		||||
 | 
			
		||||
        /*SMSView mSMSView = findViewById(R.id.viewsmssendSMSView1);
 | 
			
		||||
        mSMSView.setSMSType(SMSView.SMSType.SEND);*/
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 更新信息列表
 | 
			
		||||
    //
 | 
			
		||||
    private void setupScrollView1Height() {
 | 
			
		||||
        mScrollView1.postDelayed(new Runnable() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void run() {
 | 
			
		||||
					final ScrollView scrollView2 = (ScrollView) findViewById(R.id.activitysmsScrollView2);
 | 
			
		||||
					final BottomPositionFixedScrollView scrollView1 = (BottomPositionFixedScrollView) findViewById(R.id.activitysmsScrollView1);
 | 
			
		||||
					final View includeView = findViewById(R.id.activitysmsinclude1);
 | 
			
		||||
 | 
			
		||||
					scrollView2.post(new Runnable() {
 | 
			
		||||
							@Override
 | 
			
		||||
							public void run() {
 | 
			
		||||
								int scrollView2Height = scrollView2.getHeight();
 | 
			
		||||
								int includeHeight = includeView.getHeight();
 | 
			
		||||
								int targetHeight = Math.max(scrollView2Height - includeHeight, 0);
 | 
			
		||||
 | 
			
		||||
								LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) scrollView1.getLayoutParams();
 | 
			
		||||
								params.height = targetHeight;
 | 
			
		||||
								scrollView1.setLayoutParams(params);
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
				}
 | 
			
		||||
			}, 100);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void updateSMSView() {
 | 
			
		||||
        mSMSArrayAdapter.reLoadSMSList(SMSActivity.this, mszPhoneTo);
 | 
			
		||||
        mSMSArrayAdapter.notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 滚动消息文本框
 | 
			
		||||
    //
 | 
			
		||||
    void scrollScrollView() {
 | 
			
		||||
 | 
			
		||||
        ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
 | 
			
		||||
        ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 发送短信
 | 
			
		||||
    //
 | 
			
		||||
    void sendSMS() {
 | 
			
		||||
        // 空消息不发送
 | 
			
		||||
        String szSMSBody = metSMSBody.getText().toString();
 | 
			
		||||
        if (szSMSBody.equals("")) {
 | 
			
		||||
            Toast.makeText(getApplication(), "没有消息内容可发送。", Toast.LENGTH_SHORT).show();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 发送短信
 | 
			
		||||
        if (SMSUtil.sendMessageByInterface2(this, mszPhoneTo, szSMSBody)) {
 | 
			
		||||
            metSMSBody.setText("");
 | 
			
		||||
            new Handler().postDelayed(new Runnable(){
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void run() {
 | 
			
		||||
                        updateSMSView();
 | 
			
		||||
                        ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
                    }
 | 
			
		||||
                }, 1000);
 | 
			
		||||
            metSMSBody.clearFocus();
 | 
			
		||||
            new Handler().postDelayed(new Runnable() {
 | 
			
		||||
					@Override
 | 
			
		||||
					public void run() {
 | 
			
		||||
						updateSMSView();
 | 
			
		||||
						ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
					}
 | 
			
		||||
				}, 1000);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class SMSActivityBroadcastReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
        public SMSActivityBroadcastReceiver() {
 | 
			
		||||
            //LogUtils.d(TAG, "SMSActivityBroadcastReceiver()");
 | 
			
		||||
        }
 | 
			
		||||
        public SMSActivityBroadcastReceiver() {}
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onReceive(Context context, Intent intent) {
 | 
			
		||||
            switch (intent.getAction()) {
 | 
			
		||||
                case ACTION_NOTIFY_SMS_CHANGED :
 | 
			
		||||
                    //Toast.makeText(context, "ACTION_NOTIFY_SMS_CHANGED", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    updateSMSView();
 | 
			
		||||
                    ViewUtil.scrollScrollView(mScrollView);
 | 
			
		||||
                    //LogUtils.d(TAG, "ACTION_NOTIFY_SMS_CHANGED");
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    throw new IllegalStateException("Unexpected value: " + intent.getAction());
 | 
			
		||||
            if (ACTION_NOTIFY_SMS_CHANGED.equals(intent.getAction())) {
 | 
			
		||||
                updateSMSView();
 | 
			
		||||
                ViewUtil.scrollScrollView(mScrollView1);
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new IllegalStateException("Unexpected value: " + intent.getAction());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2024/07/19 14:30:57
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/30 14:32
 | 
			
		||||
 * @Describe 手机联系人工具类
 | 
			
		||||
 */
 | 
			
		||||
import android.content.ContentResolver;
 | 
			
		||||
@@ -12,6 +12,12 @@ import android.net.Uri;
 | 
			
		||||
import android.provider.ContactsContract;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.mymessagemanager.beans.PhoneBean;
 | 
			
		||||
import net.sourceforge.pinyin4j.PinyinHelper;
 | 
			
		||||
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
 | 
			
		||||
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
 | 
			
		||||
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
 | 
			
		||||
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
@@ -38,28 +44,137 @@ public class PhoneUtil {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 读取所有联系人
 | 
			
		||||
    //
 | 
			
		||||
    public List<PhoneBean> getPhoneList() {
 | 
			
		||||
        List<PhoneBean> listPhoneBean = new ArrayList<>();
 | 
			
		||||
        ContentResolver cr = mContext.getContentResolver();
 | 
			
		||||
        Cursor cursor = cr.query(mUriPhoneContent, new String[]{NUMBER, DISPLAY_NAME}, null, null, null);
 | 
			
		||||
        while (cursor.moveToNext()) {
 | 
			
		||||
			PhoneBean phoneBean = new PhoneBean(cursor.getString(1), cursor.getString(0).replaceAll("\\s", ""));
 | 
			
		||||
			listPhoneBean.add(phoneBean);
 | 
			
		||||
 | 
			
		||||
        if (cursor != null) {
 | 
			
		||||
            while (cursor.moveToNext()) {
 | 
			
		||||
                // 去除号码中的空格
 | 
			
		||||
                String phone = cursor.getString(0).replaceAll("\\s", "");
 | 
			
		||||
                String name = cursor.getString(1);
 | 
			
		||||
                PhoneBean phoneBean = new PhoneBean(name, phone);
 | 
			
		||||
                listPhoneBean.add(phoneBean);
 | 
			
		||||
            }
 | 
			
		||||
            cursor.close();
 | 
			
		||||
        }
 | 
			
		||||
		cursor.close();
 | 
			
		||||
 | 
			
		||||
        // 按电话号码排序
 | 
			
		||||
        Collections.sort(listPhoneBean, new Comparator<PhoneBean>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public int compare(PhoneBean o1, PhoneBean o2) {
 | 
			
		||||
                    return o1.getTelPhone().compareTo(o2.getTelPhone());
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
				@Override
 | 
			
		||||
				public int compare(PhoneBean o1, PhoneBean o2) {
 | 
			
		||||
					return o1.getTelPhone().compareTo(o2.getTelPhone());
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
        return listPhoneBean;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据联系人名称查询号码(兼容拼音查询)
 | 
			
		||||
     * @param keyword 搜索关键词(支持汉字、拼音、拼音首字母)
 | 
			
		||||
     * @return 匹配的联系人列表(包含姓名和号码)
 | 
			
		||||
     */
 | 
			
		||||
    public List<PhoneBean> getPhonesByName(String keyword) {
 | 
			
		||||
        List<PhoneBean> result = new ArrayList<>();
 | 
			
		||||
        if (keyword == null || keyword.trim().isEmpty()) {
 | 
			
		||||
            return result; // 关键词为空,返回空列表
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 获取所有联系人
 | 
			
		||||
        List<PhoneBean> allContacts = getPhoneList();
 | 
			
		||||
        // 统一转为小写,忽略大小写
 | 
			
		||||
        String keywordLower = keyword.trim().toLowerCase();
 | 
			
		||||
 | 
			
		||||
        for (PhoneBean contact : allContacts) {
 | 
			
		||||
            String name = contact.getName();
 | 
			
		||||
            if (name == null || name.isEmpty()) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 1. 直接匹配姓名(包含关键词)
 | 
			
		||||
            if (name.toLowerCase().contains(keywordLower)) {
 | 
			
		||||
                result.add(contact);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 2. 匹配姓名的全拼(包含关键词)
 | 
			
		||||
            String namePinyin = getPinyin(name).toLowerCase();
 | 
			
		||||
            if (namePinyin.contains(keywordLower)) {
 | 
			
		||||
                result.add(contact);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 3. 匹配姓名的拼音首字母(包含关键词)
 | 
			
		||||
            String namePinyinFirstLetter = getPinyinFirstLetter(name).toLowerCase();
 | 
			
		||||
            if (namePinyinFirstLetter.contains(keywordLower)) {
 | 
			
		||||
                result.add(contact);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将汉字转为全拼(不带声调,小写)
 | 
			
		||||
     * 例如:"张三" → "zhangsan"
 | 
			
		||||
     */
 | 
			
		||||
    private String getPinyin(String chinese) {
 | 
			
		||||
        StringBuilder pinyin = new StringBuilder();
 | 
			
		||||
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
 | 
			
		||||
        format.setCaseType(HanyuPinyinCaseType.LOWERCASE); // 小写
 | 
			
		||||
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); // 不带声调
 | 
			
		||||
 | 
			
		||||
        char[] chars = chinese.toCharArray();
 | 
			
		||||
        for (char c : chars) {
 | 
			
		||||
            // 如果是汉字,转换为拼音;否则直接拼接(如字母、数字、符号)
 | 
			
		||||
            if (Character.toString(c).matches("[\\u4e00-\\u9fa5]")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, format);
 | 
			
		||||
                    if (pinyinArray != null && pinyinArray.length > 0) {
 | 
			
		||||
                        pinyin.append(pinyinArray[0]); // 取第一个拼音(多音字默认取第一个)
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (BadHanyuPinyinOutputFormatCombination e) {
 | 
			
		||||
                    LogUtils.e(TAG, "拼音转换失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                pinyin.append(c);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return pinyin.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将汉字转为拼音首字母(小写)
 | 
			
		||||
     * 例如:"张三" → "zs"
 | 
			
		||||
     */
 | 
			
		||||
    private String getPinyinFirstLetter(String chinese) {
 | 
			
		||||
        StringBuilder firstLetters = new StringBuilder();
 | 
			
		||||
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
 | 
			
		||||
        format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
 | 
			
		||||
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
 | 
			
		||||
 | 
			
		||||
        char[] chars = chinese.toCharArray();
 | 
			
		||||
        for (char c : chars) {
 | 
			
		||||
            if (Character.toString(c).matches("[\\u4e00-\\u9fa5]")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, format);
 | 
			
		||||
                    if (pinyinArray != null && pinyinArray.length > 0) {
 | 
			
		||||
                        // 取拼音首字母(如"zhang" → "z")
 | 
			
		||||
                        firstLetters.append(pinyinArray[0].charAt(0));
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (BadHanyuPinyinOutputFormatCombination e) {
 | 
			
		||||
                    LogUtils.e(TAG, "拼音首字母转换失败:" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // 非汉字直接拼接首字符(如"李3" → "l3")
 | 
			
		||||
                firstLetters.append(c);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return firstLetters.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isPhoneInContacts(String szPhone) {
 | 
			
		||||
        List<PhoneBean> listPhoneDto = getPhoneList();
 | 
			
		||||
        LogUtils.d(TAG, String.format("isPhoneInContacts(...) listPhoneDto.size() %d", listPhoneDto.size()));
 | 
			
		||||
@@ -70,49 +185,56 @@ public class PhoneUtil {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    boolean isTheSamePhoneNumber(String szNum1, String szNum2) {
 | 
			
		||||
        //LogUtils.d(TAG, String.format("szNum1 %s\nszNum2 %s", szNum1, szNum2));
 | 
			
		||||
        if(szNum1.equals(szNum2)) {
 | 
			
		||||
 | 
			
		||||
    public String getNameByPhone(String szPhone) {
 | 
			
		||||
        if (szPhone == null || szPhone.equals("")) {
 | 
			
		||||
            return "";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        List<PhoneBean> listPhoneDto = getPhoneList();
 | 
			
		||||
        LogUtils.d(TAG, String.format("getNameByPhone(...) listPhoneDto.size() %d", listPhoneDto.size()));
 | 
			
		||||
        for (int i = 0; i < listPhoneDto.size(); i++) {
 | 
			
		||||
            if (isTheSamePhoneNumber(listPhoneDto.get(i).getTelPhone(), szPhone)) {
 | 
			
		||||
                return listPhoneDto.get(i).getName();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isTheSamePhoneNumber(String szNum1, String szNum2) {
 | 
			
		||||
        if (szNum1.equals(szNum2)) {
 | 
			
		||||
            LogUtils.d(TAG, "szNum1.equals(szNum2)");
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if(UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum1)) {
 | 
			
		||||
            if(szNum1.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum2))) {
 | 
			
		||||
 | 
			
		||||
        if (UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum1)) {
 | 
			
		||||
            if (szNum1.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum2))) {
 | 
			
		||||
                LogUtils.d(TAG, "szNum1.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum2))");
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if(UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum2)) {
 | 
			
		||||
            if(szNum2.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum1))) {
 | 
			
		||||
 | 
			
		||||
        if (UnitAreaUtils.getInstance(mContext).isCurrentUnitAreaNumber(szNum2)) {
 | 
			
		||||
            if (szNum2.equals(UnitAreaUtils.getInstance(mContext).genCurrentUnitAreaNumber(szNum1))) {
 | 
			
		||||
                LogUtils.d(TAG, "szNum2.equals(UnitAreaUtils.genCurrentUnitAreaNumber(szNum1))");
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        LogUtils.d(TAG, "isTheSamePhoneNumber(...) return false;");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 检验电话号码是否是数字
 | 
			
		||||
    //
 | 
			
		||||
    public static boolean isPhoneByDigit(String szPhone) {
 | 
			
		||||
        if(!RegexPPiUtils.isPPiOK(szPhone)) {
 | 
			
		||||
        if (!RegexPPiUtils.isPPiOK(szPhone)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        //String text = "这里是一些任意的文本内容";
 | 
			
		||||
        String regex = "[+]?\\d+";
 | 
			
		||||
        Pattern pattern = Pattern.compile(regex);
 | 
			
		||||
        Matcher matcher = pattern.matcher(szPhone);
 | 
			
		||||
        LogUtils.d(TAG, String.format("matcher.matches() : %s", matcher.matches()));
 | 
			
		||||
        /*if (matcher.matches()) {
 | 
			
		||||
         System.out.println("文本满足该正则表达式模式");
 | 
			
		||||
         } else {
 | 
			
		||||
         System.out.println("文本不满足该正则表达式模式");
 | 
			
		||||
         }*/
 | 
			
		||||
        return matcher.matches();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,9 @@ import cc.winboll.studio.mymessagemanager.beans.AppConfigBean;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
 | 
			
		||||
public class UnitAreaUtils {
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "UnitAreaUtils";
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    static UnitAreaUtils _UnitAreaUtils;
 | 
			
		||||
    Context mContext;
 | 
			
		||||
 | 
			
		||||
@@ -26,19 +26,25 @@ public class UnitAreaUtils {
 | 
			
		||||
        }
 | 
			
		||||
        return _UnitAreaUtils;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public boolean isCurrentUnitAreaNumber(String szPhoneNumer) {
 | 
			
		||||
        String szUnitArea = getUnitArea();
 | 
			
		||||
        LogUtils.d(TAG, String.format("szPhoneNumer.substring(1,3) %s", szPhoneNumer.substring(1,3)));
 | 
			
		||||
        return szPhoneNumer.substring(1,3).equals(szUnitArea);
 | 
			
		||||
		try {
 | 
			
		||||
			String szPhoneNumerUnitArea = szPhoneNumer.substring(1, 3);
 | 
			
		||||
			LogUtils.d(TAG, String.format("szPhoneNumerUnitArea %s", szPhoneNumerUnitArea));
 | 
			
		||||
			return szPhoneNumerUnitArea.equals(szUnitArea);
 | 
			
		||||
		} catch (StringIndexOutOfBoundsException e) {
 | 
			
		||||
			LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
		}
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public String genCurrentUnitAreaNumber(String szPhoneNumer) {
 | 
			
		||||
        String szUnitArea = getUnitArea();
 | 
			
		||||
        LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea));
 | 
			
		||||
        return "+" + szUnitArea + szPhoneNumer;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    String getUnitArea() {
 | 
			
		||||
        String szUnitArea = AppConfigUtil.getInstance(mContext).mAppConfigBean.getCountryCode();
 | 
			
		||||
        LogUtils.d(TAG, String.format("szUnitArea %s", szUnitArea));
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,125 @@
 | 
			
		||||
package cc.winboll.studio.mymessagemanager.views;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/08/23 00:39
 | 
			
		||||
 * @Describe 多级拉动响应自定义控件
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.util.AttributeSet;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.widget.ScrollView;
 | 
			
		||||
 | 
			
		||||
public class BottomPositionFixedScrollView extends ScrollView {
 | 
			
		||||
	public static final String TAG = "BottomPositionFixedScrollView";
 | 
			
		||||
	// 记录底部对应的内容绝对位置(即底部位置在内容中的y坐标,该位置需始终保持在视图底部)
 | 
			
		||||
	private int mBottomContentY = 0;
 | 
			
		||||
	// 标记是否是首次布局(避免初始加载误触发)
 | 
			
		||||
	private boolean isFirstLayout = true;
 | 
			
		||||
 | 
			
		||||
	public BottomPositionFixedScrollView(Context context) {
 | 
			
		||||
		super(context);
 | 
			
		||||
		init();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public BottomPositionFixedScrollView(Context context, AttributeSet attrs) {
 | 
			
		||||
		super(context, attrs);
 | 
			
		||||
		init();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public BottomPositionFixedScrollView(Context context, AttributeSet attrs, int defStyle) {
 | 
			
		||||
		super(context, attrs, defStyle);
 | 
			
		||||
		init();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void init() {
 | 
			
		||||
		// 监听布局变化(高度改变时触发)
 | 
			
		||||
		getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onGlobalLayout() {
 | 
			
		||||
					if (isFirstLayout) {
 | 
			
		||||
						isFirstLayout = false;
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
					// 布局变化后,恢复底部位置
 | 
			
		||||
					restoreBottomPosition();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 重写滚动事件,记录“底部对应的内容绝对位置”
 | 
			
		||||
	 * (即当前视图底部边缘对应的内容y坐标,该坐标需始终保持在视图底部)
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
 | 
			
		||||
		super.onScrollChanged(l, t, oldl, oldt);
 | 
			
		||||
		if (getChildCount() == 0) {
 | 
			
		||||
			mBottomContentY = 0;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 内容总高度
 | 
			
		||||
		int contentHeight = getChildAt(0).getMeasuredHeight();
 | 
			
		||||
		// 视图可视高度(自身高度)
 | 
			
		||||
		int scrollViewHeight = getMeasuredHeight();
 | 
			
		||||
		// 当前视图底部边缘对应的内容y坐标 = 顶部滚动距离(t) + 可视高度
 | 
			
		||||
		// (该坐标就是“底部内容的绝对位置”,需始终保持在视图底部)
 | 
			
		||||
		mBottomContentY = t + scrollViewHeight;
 | 
			
		||||
 | 
			
		||||
		// 避免超过内容总高度(比如内容不足一屏时,底部最多到内容底部)
 | 
			
		||||
		if (mBottomContentY > contentHeight) {
 | 
			
		||||
			mBottomContentY = contentHeight;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 恢复底部位置:让原记录的“底部内容绝对位置”仍保持在视图底部
 | 
			
		||||
	 */
 | 
			
		||||
	private void restoreBottomPosition() {
 | 
			
		||||
		if (getChildCount() == 0) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 新的内容总高度
 | 
			
		||||
		int newContentHeight = getChildAt(0).getMeasuredHeight();
 | 
			
		||||
		// 新的视图可视高度
 | 
			
		||||
		int newScrollViewHeight = getMeasuredHeight();
 | 
			
		||||
 | 
			
		||||
		// 目标:让原mBottomContentY(底部内容绝对位置)仍位于视图底部
 | 
			
		||||
		// 此时需要的顶部滚动距离 = mBottomContentY - 新的可视高度
 | 
			
		||||
		int targetScrollY = mBottomContentY - newScrollViewHeight;
 | 
			
		||||
 | 
			
		||||
		// 边界修正:
 | 
			
		||||
		// 1. 不能小于0(避免滚动到负数位置)
 | 
			
		||||
		// 2. 不能大于“最大可滚动距离”(内容高度 - 可视高度,避免超出内容范围)
 | 
			
		||||
		int maxScrollY = Math.max(newContentHeight - newScrollViewHeight, 0);
 | 
			
		||||
		targetScrollY = Math.max(targetScrollY, 0);
 | 
			
		||||
		targetScrollY = Math.min(targetScrollY, maxScrollY);
 | 
			
		||||
 | 
			
		||||
		// 滚动到目标位置,保持底部内容位置不变
 | 
			
		||||
		smoothScrollTo(0, targetScrollY);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 外部手动设置底部内容绝对位置(可选)
 | 
			
		||||
	 */
 | 
			
		||||
	public void setBottomContentY(int bottomContentY) {
 | 
			
		||||
		if (getChildCount() == 0) {
 | 
			
		||||
			mBottomContentY = bottomContentY;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		// 限制不超过内容总高度
 | 
			
		||||
		int contentHeight = getChildAt(0).getMeasuredHeight();
 | 
			
		||||
		mBottomContentY = Math.min(bottomContentY, contentHeight);
 | 
			
		||||
		restoreBottomPosition();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获取当前底部内容绝对位置(可选)
 | 
			
		||||
	 */
 | 
			
		||||
	public int getBottomContentY() {
 | 
			
		||||
		return mBottomContentY;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -21,25 +21,63 @@
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/activitycomposesmsRelativeLayout1">
 | 
			
		||||
 | 
			
		||||
			<TextView
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:text="SMS TO : "
 | 
			
		||||
				android:id="@+id/activitycomposesmsTextView1"
 | 
			
		||||
				android:layout_alignParentLeft="true"
 | 
			
		||||
				android:layout_marginLeft="10dp"
 | 
			
		||||
				android:layout_centerVertical="true"/>
 | 
			
		||||
 | 
			
		||||
			<EditText
 | 
			
		||||
				android:layout_toRightOf="@id/activitycomposesmsTextView1"
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:inputType="phone"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:ems="10"
 | 
			
		||||
				android:id="@+id/activitycomposesmsEditText1"
 | 
			
		||||
				android:id="@+id/activitycomposesmsLinearLayout1"
 | 
			
		||||
				android:gravity="center_vertical"
 | 
			
		||||
				android:layout_alignParentRight="true"
 | 
			
		||||
				android:layout_marginRight="10dp"
 | 
			
		||||
				android:layout_centerVertical="true"/>
 | 
			
		||||
				android:layout_marginLeft="10dp"
 | 
			
		||||
				android:layout_alignParentLeft="true">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="(拼音搜索):"/>
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="80dp"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/activitycomposesmsEditText2"/>
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:layout_alignParentTop="true"
 | 
			
		||||
					android:layout_toRightOf="@id/activitycomposesmsEditText2"
 | 
			
		||||
					android:id="@+id/activitycomposesmsTextView2"
 | 
			
		||||
					android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_below="@id/activitycomposesmsLinearLayout1"
 | 
			
		||||
				android:layout_alignParentRight="true"
 | 
			
		||||
				android:layout_marginRight="10dp"
 | 
			
		||||
				android:layout_marginLeft="10dp"
 | 
			
		||||
				android:layout_alignParentLeft="true"
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="center_vertical">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="(SMS TO) :"
 | 
			
		||||
					android:id="@+id/activitycomposesmsTextView1"/>
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:inputType="phone"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:id="@+id/activitycomposesmsEditText1"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		</RelativeLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="60dp"
 | 
			
		||||
			android:padding="10dp"
 | 
			
		||||
			android:text="@string/text_onlyreceivecontacts"
 | 
			
		||||
			android:text="@string/text_norulesreceivecontacts"
 | 
			
		||||
			android:id="@+id/activitymainSwitchView2"/>
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.mymessagemanager.views.ConfirmSwitchView
 | 
			
		||||
 
 | 
			
		||||
@@ -10,36 +10,40 @@
 | 
			
		||||
		android:layout_height="@dimen/toolbar_height"
 | 
			
		||||
		android:id="@+id/activitysmsASupportToolbar1"/>
 | 
			
		||||
 | 
			
		||||
	<RelativeLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
	<ScrollView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="match_parent"
 | 
			
		||||
		android:paddingBottom="10dp">
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
		android:id="@+id/activitysmsScrollView2">
 | 
			
		||||
 | 
			
		||||
		<ScrollView
 | 
			
		||||
			android:layout_alignParentTop="true"
 | 
			
		||||
			android:layout_above="@+id/activitysmsinclude1"
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			android:orientation="vertical"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="0dp"
 | 
			
		||||
			android:id="@+id/activitysmsinphoneScrollView1"
 | 
			
		||||
			android:layout_weight="1.0"
 | 
			
		||||
			android:isScrollContainer="false">
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/activitysmsLinearLayout1">
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView
 | 
			
		||||
			<cc.winboll.studio.mymessagemanager.views.BottomPositionFixedScrollView
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="520dp"
 | 
			
		||||
				android:isScrollContainer="false"
 | 
			
		||||
				android:id="@+id/activitysmsScrollView1">
 | 
			
		||||
 | 
			
		||||
				<cc.winboll.studio.mymessagemanager.views.SMSListViewForScrollView
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/activitysmsSMSListViewForScrollView1"/>
 | 
			
		||||
 | 
			
		||||
			</cc.winboll.studio.mymessagemanager.views.BottomPositionFixedScrollView>
 | 
			
		||||
 | 
			
		||||
			<include
 | 
			
		||||
				layout="@layout/view_smssend"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:id="@+id/activitysmsinphoneListView1"/>
 | 
			
		||||
				android:id="@+id/activitysmsinclude1"/>
 | 
			
		||||
			
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		</ScrollView>
 | 
			
		||||
 | 
			
		||||
		<include
 | 
			
		||||
			android:layout_alignParentBottom="true"
 | 
			
		||||
			layout="@layout/view_smssend"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/activitysmsinclude1"/>
 | 
			
		||||
 | 
			
		||||
	</RelativeLayout>
 | 
			
		||||
	</ScrollView>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,18 @@
 | 
			
		||||
	android:layout_height="wrap_content"
 | 
			
		||||
	android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:gravity="center"
 | 
			
		||||
		android:textSize="20sp"
 | 
			
		||||
		android:id="@+id/viewsmssendpart1TextView1"/>
 | 
			
		||||
	
 | 
			
		||||
	<cc.winboll.studio.libaes.views.AOHPCTCSeekBar
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/viewsmssendpart1AOHPCTCSeekBar1"/>
 | 
			
		||||
 | 
			
		||||
	<EditText
 | 
			
		||||
        android:scrollbars="vertical"
 | 
			
		||||
        android:maxHeight="150dp"
 | 
			
		||||
@@ -17,17 +29,6 @@
 | 
			
		||||
		android:id="@+id/viewsmssendpart1EditText1"
 | 
			
		||||
		android:background="@drawable/bg_frame"/>
 | 
			
		||||
 | 
			
		||||
	<cc.winboll.studio.libaes.views.AOHPCTCSeekBar
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/viewsmssendpart1AOHPCTCSeekBar1"/>
 | 
			
		||||
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:gravity="center"
 | 
			
		||||
		android:textSize="20sp"
 | 
			
		||||
		android:id="@+id/viewsmssendpart1TextView1"/>
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@
 | 
			
		||||
    <string name="text_item_rule_clean">清理设置</string>
 | 
			
		||||
    <string name="text_sendsms">发送短信</string>
 | 
			
		||||
    <string name="text_mainservice">短信服务管理总开关</string>
 | 
			
		||||
    <string name="text_onlyreceivecontacts">只接收联系人短信</string>
 | 
			
		||||
    <string name="text_norulesreceivecontacts">无限制接收联系人短信</string>
 | 
			
		||||
    <string name="text_usingtts">使用TTS语音播报</string>
 | 
			
		||||
    <string name="text_usingttsrule">使用TTS语音自定义规则</string>
 | 
			
		||||
    <string name="text_iamhere">短信管理服务已启动。</string>
 | 
			
		||||
@@ -41,6 +41,6 @@
 | 
			
		||||
    <string name="text_appsettings">应用设置</string>
 | 
			
		||||
    <string name="text_ttsplaydelaytimes">TTS播放延迟时间(秒):</string>
 | 
			
		||||
    <string name="msg_newsms">接收到新的消息。</string>
 | 
			
		||||
    <string name="msg_100sendmsg">>>>拉图标动到 100% 以发送信息。>>></string>
 | 
			
		||||
    <string name="msg_100applysettings">>>>拉图标动到 100% 应用设置。>>></string>
 | 
			
		||||
    <string name="msg_100sendmsg">>>>拉动到100%可发信息>>></string>
 | 
			
		||||
    <string name="msg_100applysettings">>>>拉动到100%可应用设置>>></string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
    <string name="text_item_rule_clean">Clean Setting</string>
 | 
			
		||||
    <string name="text_sendsms">Send SMS</string>
 | 
			
		||||
    <string name="text_mainservice">Main Service</string>
 | 
			
		||||
    <string name="text_onlyreceivecontacts">Only Receive Contacts</string>
 | 
			
		||||
    <string name="text_norulesreceivecontacts">No rules Receive Contacts</string>
 | 
			
		||||
    <string name="text_usingtts">Using TTS</string>
 | 
			
		||||
    <string name="text_usingttsrule">Using TTS Rule</string>
 | 
			
		||||
    <string name="text_iamhere">The main service is start.</string>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Sun Jun 08 21:21:11 HKT 2025
 | 
			
		||||
stageCount=1
 | 
			
		||||
#Thu Jul 24 10:16:27 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.1
 | 
			
		||||
publishVersion=15.1.0
 | 
			
		||||
publishVersion=15.1.1
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.1.1
 | 
			
		||||
baseBetaVersion=15.1.2
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,11 @@
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="cc.winboll.studio.numtable">
 | 
			
		||||
 | 
			
		||||
	<!-- Android 13及以下需要的权限 -->
 | 
			
		||||
	<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
 | 
			
		||||
	<!-- 可选:查询快捷方式是否存在(部分机型需要) -->
 | 
			
		||||
	<uses-permission android:name="com.android.launcher.permission.QUERY_ALL_PACKAGES" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
        android:icon="@drawable/ic_launcher"
 | 
			
		||||
@@ -29,9 +34,7 @@
 | 
			
		||||
        <meta-data
 | 
			
		||||
            android:name="android.max_aspect"
 | 
			
		||||
            android:value="4.0"/>
 | 
			
		||||
 | 
			
		||||
        <activity android:name=".GlobalApplication$CrashActivity"/>
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,20 @@
 | 
			
		||||
package cc.winboll.studio.numtable;
 | 
			
		||||
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.pm.ShortcutInfo;
 | 
			
		||||
import android.content.pm.ShortcutManager;
 | 
			
		||||
import android.graphics.drawable.Icon;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.RadioButton;
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.numtable.R;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
public class MainActivity extends AppCompatActivity {
 | 
			
		||||
 | 
			
		||||
@@ -19,10 +29,99 @@ public class MainActivity extends AppCompatActivity {
 | 
			
		||||
		setSupportActionBar(toolbar);
 | 
			
		||||
 | 
			
		||||
        mLogView = findViewById(R.id.logview);
 | 
			
		||||
        
 | 
			
		||||
        ToastUtils.show("onCreate");
 | 
			
		||||
 | 
			
		||||
		// 初始化创建快捷方式按钮
 | 
			
		||||
		Button btnCreateShortcut = findViewById(R.id.btn_create_shortcut);
 | 
			
		||||
		btnCreateShortcut.setOnClickListener(new View.OnClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onClick(View v) {
 | 
			
		||||
					createDesktopShortcut();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		((RadioButton)findViewById(R.id.rb_right)).setChecked(true);
 | 
			
		||||
		((RadioButton)findViewById(R.id.rb_left)).setChecked(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	public void onDirectionClick(View view) {
 | 
			
		||||
		switch (view.getId()) {
 | 
			
		||||
			case R.id.rb_right:
 | 
			
		||||
				{
 | 
			
		||||
					((RadioButton)findViewById(R.id.rb_left)).setChecked(false);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			case R.id.rb_left:
 | 
			
		||||
				{
 | 
			
		||||
					((RadioButton)findViewById(R.id.rb_right)).setChecked(false);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 创建桌面快捷方式
 | 
			
		||||
	 */
 | 
			
		||||
	private void createDesktopShortcut() {
 | 
			
		||||
		// 1. 创建启动目标Activity的Intent(通常是你的主Activity)
 | 
			
		||||
		Intent targetIntent = new Intent(this, MainActivity.class); // 替换为你的主Activity
 | 
			
		||||
		targetIntent.setAction(Intent.ACTION_MAIN);
 | 
			
		||||
		targetIntent.addCategory(Intent.CATEGORY_LAUNCHER);
 | 
			
		||||
		targetIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
 | 
			
		||||
 | 
			
		||||
		// 2. 创建快捷方式的Intent
 | 
			
		||||
		Intent shortcutIntent = new Intent();
 | 
			
		||||
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
 | 
			
		||||
			// 生成随机UUID
 | 
			
		||||
			UUID uuid = UUID.randomUUID();
 | 
			
		||||
			// 转换为字符串
 | 
			
		||||
			String uuidStr = uuid.toString();
 | 
			
		||||
			// Android 8.0及以上:使用ShortcutManager
 | 
			
		||||
			ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
 | 
			
		||||
			if (shortcutManager.isRequestPinShortcutSupported()) {
 | 
			
		||||
				if (((RadioButton)findViewById(R.id.rb_right)).isChecked()) {
 | 
			
		||||
					// 创建快捷方式信息
 | 
			
		||||
					ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(this, uuidStr) // 唯一ID
 | 
			
		||||
						.setShortLabel(">>") // 短标签(显示在桌面)
 | 
			
		||||
						.setLongLabel("在右边 >>") // 长标签(长按显示)
 | 
			
		||||
						.setIcon(Icon.createWithResource(this, R.drawable.ic_point_right)) // 图标
 | 
			
		||||
						.setIntent(targetIntent)
 | 
			
		||||
						.build();
 | 
			
		||||
					// 发送创建请求
 | 
			
		||||
					shortcutManager.requestPinShortcut(shortcutInfo, null);
 | 
			
		||||
				} else {
 | 
			
		||||
					ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(this, uuidStr) // 唯一ID
 | 
			
		||||
						.setShortLabel("<<") // 短标签(显示在桌面)
 | 
			
		||||
						.setLongLabel("在左边 <<") // 长标签(长按显示)
 | 
			
		||||
						.setIcon(Icon.createWithResource(this, R.drawable.ic_point_left)) // 图标
 | 
			
		||||
						.setIntent(targetIntent)
 | 
			
		||||
						.build();
 | 
			
		||||
					// 发送创建请求
 | 
			
		||||
					shortcutManager.requestPinShortcut(shortcutInfo, null);
 | 
			
		||||
				}
 | 
			
		||||
				ToastUtils.show("已请求创建快捷方式");
 | 
			
		||||
			} else {
 | 
			
		||||
				ToastUtils.show("当前设备不支持创建快捷方式");
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Android 7.1及以下:使用旧版广播方式
 | 
			
		||||
			shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, targetIntent);
 | 
			
		||||
			shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "应用快捷方式"); // 快捷方式名称
 | 
			
		||||
			// 设置图标(使用应用图标)
 | 
			
		||||
			shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, 
 | 
			
		||||
									Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher));
 | 
			
		||||
			// 标记为创建快捷方式
 | 
			
		||||
			shortcutIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
 | 
			
		||||
			// 防止重复创建
 | 
			
		||||
			shortcutIntent.putExtra("duplicate", false);
 | 
			
		||||
 | 
			
		||||
			// 发送广播创建快捷方式
 | 
			
		||||
			sendBroadcast(shortcutIntent);
 | 
			
		||||
			ToastUtils.show("快捷方式已创建");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								numtable/src/main/res/drawable/ic_point_left.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								numtable/src/main/res/drawable/ic_point_left.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="512dp"
 | 
			
		||||
    android:height="512dp"
 | 
			
		||||
    android:viewportWidth="512"
 | 
			
		||||
    android:viewportHeight="512">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF3D8A1C"
 | 
			
		||||
        android:strokeColor="#FFF5DD00"
 | 
			
		||||
        android:strokeWidth="5.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M401.84 141.95C361.52 236.12 354.86 278.98 399.2 382.12 359.53 383.01 245.24 289.83 245.38 265.73 247.41 236.75 361.69 143.4 401.84 141.95Z"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF3D8A1C"
 | 
			
		||||
        android:strokeColor="#FFF5DD00"
 | 
			
		||||
        android:strokeWidth="5.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M276.81 141.95C236.49 236.12 229.83 278.98 274.17 382.12 234.5 383.01 120.2 289.83 120.35 265.73 122.37 236.75 236.66 143.4 276.81 141.95Z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										20
									
								
								numtable/src/main/res/drawable/ic_point_right.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								numtable/src/main/res/drawable/ic_point_right.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="512dp"
 | 
			
		||||
    android:height="512dp"
 | 
			
		||||
    android:viewportWidth="512"
 | 
			
		||||
    android:viewportHeight="512">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF3D8A1C"
 | 
			
		||||
        android:strokeColor="#FFF5DD00"
 | 
			
		||||
        android:strokeWidth="5.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M103.57 145.68C143.89 239.85 150.55 282.71 106.21 385.85 145.88 386.74 260.17 293.56 260.03 269.46 258 240.48 143.72 147.13 103.57 145.68Z"/>
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF3D8A1C"
 | 
			
		||||
        android:strokeColor="#FFF5DD00"
 | 
			
		||||
        android:strokeWidth="5.0"
 | 
			
		||||
        android:strokeLineCap="round"
 | 
			
		||||
        android:strokeMiterLimit="10"
 | 
			
		||||
        android:pathData="M228.6 145.68C268.92 239.85 275.58 282.71 231.24 385.85 270.91 386.74 385.21 293.56 385.06 269.46 383.04 240.48 268.75 147.13 228.6 145.68Z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -23,14 +23,44 @@
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
		android:gravity="center_vertical|center_horizontal">
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			android:orientation="horizontal"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="NumTable"
 | 
			
		||||
			android:textAppearance="?android:attr/textAppearanceLarge"/>
 | 
			
		||||
			android:gravity="right|center_vertical"
 | 
			
		||||
			android:layout_gravity="center_vertical">
 | 
			
		||||
 | 
			
		||||
			<ImageView
 | 
			
		||||
				android:layout_width="60dp"
 | 
			
		||||
				android:layout_height="60dp"
 | 
			
		||||
				android:background="@drawable/ic_point_right"/>
 | 
			
		||||
 | 
			
		||||
			<RadioButton
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:id="@+id/rb_right"
 | 
			
		||||
				android:onClick="onDirectionClick"/>
 | 
			
		||||
 | 
			
		||||
			<ImageView
 | 
			
		||||
				android:layout_width="60dp"
 | 
			
		||||
				android:layout_height="60dp"
 | 
			
		||||
				android:background="@drawable/ic_point_left"/>
 | 
			
		||||
 | 
			
		||||
			<RadioButton
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:id="@+id/rb_left"
 | 
			
		||||
				android:onClick="onDirectionClick"/>
 | 
			
		||||
 | 
			
		||||
			<Button
 | 
			
		||||
				android:id="@+id/btn_create_shortcut"
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:text="创建桌面快捷方式"/>
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -45,9 +45,9 @@ android {
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.6.0'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.3.4'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.7.6'
 | 
			
		||||
    api 'cc.winboll.studio:libaes:15.9.3'
 | 
			
		||||
    api 'cc.winboll.studio:libapputils:15.8.5'
 | 
			
		||||
    api 'cc.winboll.studio:libappbase:15.9.5'
 | 
			
		||||
    
 | 
			
		||||
    // 吐司提示库
 | 
			
		||||
    api 'com.github.getActivity:ToastUtils:10.5'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Thu May 29 09:43:37 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Sun Aug 31 06:21:48 CST 2025
 | 
			
		||||
stageCount=12
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.4
 | 
			
		||||
publishVersion=15.4.1
 | 
			
		||||
publishVersion=15.4.11
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.4.2
 | 
			
		||||
baseBetaVersion=15.4.12
 | 
			
		||||
 
 | 
			
		||||
@@ -121,6 +121,8 @@
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.powerbell.activities.AboutActivity"/>
 | 
			
		||||
 | 
			
		||||
        <activity android:name="cc.winboll.studio.powerbell.activities.PixelPickerActivity"/>
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
</manifest>
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package cc.winboll.studio.powerbell;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.os.Environment;
 | 
			
		||||
import android.view.Gravity;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver;
 | 
			
		||||
@@ -18,7 +19,7 @@ public class App extends GlobalApplication {
 | 
			
		||||
    static AppCacheUtils _mAppCacheUtils;
 | 
			
		||||
    GlobalApplicationReceiver mReceiver;
 | 
			
		||||
    static String szTempDir = "";
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public static String getTempDirPath() {
 | 
			
		||||
        return szTempDir;
 | 
			
		||||
    }
 | 
			
		||||
@@ -26,15 +27,24 @@ public class App extends GlobalApplication {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
 | 
			
		||||
        // 临时文件夹方案1
 | 
			
		||||
        // 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
 | 
			
		||||
        File picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
 | 
			
		||||
        // 定义目标文件路径(在Pictures目录下创建"PowerBell"子文件夹及文件)
 | 
			
		||||
        File powerBellDir = new File(picturesDir, "PowerBell");
 | 
			
		||||
        
 | 
			
		||||
        // 初始化临时文件夹目录
 | 
			
		||||
        File fTempDir = new File(getExternalCacheDir(), "TempDir");
 | 
			
		||||
        if(!fTempDir.exists()) {
 | 
			
		||||
            fTempDir.mkdirs();
 | 
			
		||||
        // 临时文件夹方案2 <图片保存失败>
 | 
			
		||||
        // 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
 | 
			
		||||
        //File powerBellDir = getExternalFilesDir("TempDir");
 | 
			
		||||
 | 
			
		||||
        // 先创建文件夹(如果不存在)
 | 
			
		||||
        if (!powerBellDir.exists()) {
 | 
			
		||||
            powerBellDir.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
        szTempDir = fTempDir.getAbsolutePath();
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
        szTempDir = powerBellDir.getAbsolutePath();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // 初始化 Toast 框架
 | 
			
		||||
        ToastUtils.init(this);
 | 
			
		||||
        // 设置 Toast 布局样式
 | 
			
		||||
@@ -45,7 +55,7 @@ public class App extends GlobalApplication {
 | 
			
		||||
        // 设置数据配置存储工具
 | 
			
		||||
        _mAppConfigUtils = getAppConfigUtils(this);
 | 
			
		||||
        _mAppCacheUtils = getAppCacheUtils(this);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        mReceiver = new GlobalApplicationReceiver(this);
 | 
			
		||||
        mReceiver.registerAction();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,23 +10,27 @@ import android.os.Bundle;
 | 
			
		||||
import android.provider.MediaStore;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import android.widget.RelativeLayout;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AToolbar;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.powerbell.MainActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.AboutActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.BatteryReporterActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
 | 
			
		||||
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
 | 
			
		||||
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
 | 
			
		||||
 | 
			
		||||
public class MainActivity extends WinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
public class MainActivity extends Activity {
 | 
			
		||||
    public static final String TAG = "MainActivity";
 | 
			
		||||
    public static final int BACKGROUND_PICTURE_REQUEST_CODE = 0;
 | 
			
		||||
 | 
			
		||||
    public static MainActivity _mMainActivity;
 | 
			
		||||
    LogView mLogView;
 | 
			
		||||
    //LogView mLogView;
 | 
			
		||||
    //ArrayList<Fragment> mlistFragment;
 | 
			
		||||
    App mApplication;
 | 
			
		||||
    //AppConfigUtils mAppConfigUtils;
 | 
			
		||||
@@ -35,6 +39,16 @@ public class MainActivity extends Activity {
 | 
			
		||||
    MainViewFragment mMainViewFragment;
 | 
			
		||||
    AToolbar mAToolbar;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Activity getActivity() {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTag() {
 | 
			
		||||
        return TAG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        //LogUtils.d(TAG, "onCreate(...)");
 | 
			
		||||
@@ -42,10 +56,10 @@ public class MainActivity extends Activity {
 | 
			
		||||
        setContentView(R.layout.activity_main);
 | 
			
		||||
 | 
			
		||||
        // 设置调试日志
 | 
			
		||||
        mLogView = findViewById(R.id.logview);
 | 
			
		||||
        mLogView.start();
 | 
			
		||||
        //LogUtils.d(TAG, "LogView Start.");
 | 
			
		||||
        mLogView.updateLogView();
 | 
			
		||||
//        mLogView = findViewById(R.id.logview);
 | 
			
		||||
//        mLogView.start();
 | 
			
		||||
//        //LogUtils.d(TAG, "LogView Start.");
 | 
			
		||||
//        mLogView.updateLogView();
 | 
			
		||||
 | 
			
		||||
        _mMainActivity = MainActivity.this;
 | 
			
		||||
        mApplication = (App) getApplication();
 | 
			
		||||
@@ -117,8 +131,8 @@ public class MainActivity extends Activity {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        // 回到窗口自动取消提醒消息
 | 
			
		||||
        //NotificationHelper.cancelRemindNotification(this);
 | 
			
		||||
 | 
			
		||||
        reloadBackground();
 | 
			
		||||
		setBackgroundColor();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Menu icons are inflated just as they were with actionbar
 | 
			
		||||
@@ -157,6 +171,8 @@ public class MainActivity extends Activity {
 | 
			
		||||
            Intent intent = new Intent();
 | 
			
		||||
            intent.setClass(this, BackgroundPictureActivity.class);
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
        } else if (menuItemId == R.id.action_log) {
 | 
			
		||||
            App.getWinBoLLActivityManager().startLogActivity(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
@@ -193,4 +209,12 @@ public class MainActivity extends Activity {
 | 
			
		||||
            moveTaskToBack(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	void setBackgroundColor() {
 | 
			
		||||
		BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(MainActivity.this);
 | 
			
		||||
		BackgroundPictureBean bean = utils.getBackgroundPictureBean();
 | 
			
		||||
		int nPixelColor = bean.getPixelColor();
 | 
			
		||||
		RelativeLayout mainLayout = findViewById(R.id.activitymainRelativeLayout1);
 | 
			
		||||
		mainLayout.setBackgroundColor(nPixelColor);
 | 
			
		||||
	}
 | 
			
		||||
} 
 | 
			
		||||
 
 | 
			
		||||
@@ -53,11 +53,11 @@ public class AboutActivity extends Activity {
 | 
			
		||||
        appInfo.setAppName(getString(R.string.app_name));
 | 
			
		||||
        appInfo.setAppIcon(R.drawable.ic_launcher);
 | 
			
		||||
        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=PowerBell");
 | 
			
		||||
        appInfo.setAppHomePage("https://discuz.winboll.cc/forum.php?mod=viewthread&tid=1");
 | 
			
		||||
        appInfo.setAppAPKName("PowerBell");
 | 
			
		||||
        appInfo.setAppAPKFolderName("PowerBell");
 | 
			
		||||
        return new AboutView(mContext, appInfo);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,27 @@
 | 
			
		||||
package cc.winboll.studio.powerbell.activities;
 | 
			
		||||
 | 
			
		||||
import android.Manifest;
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.graphics.Bitmap;
 | 
			
		||||
import android.graphics.BitmapFactory;
 | 
			
		||||
import android.graphics.drawable.Drawable;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.provider.MediaStore;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import android.widget.RelativeLayout;
 | 
			
		||||
import androidx.core.app.ActivityCompat;
 | 
			
		||||
import androidx.core.content.ContextCompat;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AToolbar;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
 | 
			
		||||
import cc.winboll.studio.powerbell.App;
 | 
			
		||||
import cc.winboll.studio.powerbell.R;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
 | 
			
		||||
import cc.winboll.studio.powerbell.dialogs.BackgroundPicturePreviewDialog;
 | 
			
		||||
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
 | 
			
		||||
@@ -24,42 +29,45 @@ import cc.winboll.studio.powerbell.utils.FileUtils;
 | 
			
		||||
import cc.winboll.studio.powerbell.utils.UriUtil;
 | 
			
		||||
import java.io.BufferedOutputStream;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
 | 
			
		||||
public class BackgroundPictureActivity extends Activity
 | 
			
		||||
implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
public class BackgroundPictureActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "BackgroundPictureActivity";
 | 
			
		||||
 | 
			
		||||
    public BackgroundPictureUtils mBackgroundPictureUtils;
 | 
			
		||||
 | 
			
		||||
    // 图片选择请求
 | 
			
		||||
    // 图片选择请求码
 | 
			
		||||
    public static final int REQUEST_SELECT_PICTURE = 0;
 | 
			
		||||
    // 照相选择请求
 | 
			
		||||
    public static final int REQUEST_TAKE_PHOTO = 1;
 | 
			
		||||
    // 图片裁剪选择请求
 | 
			
		||||
    public static final int REQUEST_CROP_IMAGE = 2;
 | 
			
		||||
    private static final int STORAGE_PERMISSION_REQUEST = 100;
 | 
			
		||||
 | 
			
		||||
    AToolbar mAToolbar;
 | 
			
		||||
    // 所有图片存储的文件夹
 | 
			
		||||
    File mfBackgroundDir;
 | 
			
		||||
    // 拍照与剪裁的文件夹
 | 
			
		||||
    File mfPictureDir;
 | 
			
		||||
    // 拍照文件类
 | 
			
		||||
    File mfTakePhoto;
 | 
			
		||||
    // 接收到的图片文件类
 | 
			
		||||
    public File mfRecivedPicture;
 | 
			
		||||
    // 剪裁文件类
 | 
			
		||||
    File mfTempCropPicture;
 | 
			
		||||
    // 剪裁接收后的文件的文件名
 | 
			
		||||
    private AToolbar mAToolbar;
 | 
			
		||||
    private File mfBackgroundDir;       // 背景图片存储文件夹
 | 
			
		||||
    private File mfPictureDir;          // 拍照与剪裁临时文件夹
 | 
			
		||||
    private File mfTakePhoto;           // 拍照文件
 | 
			
		||||
    private File mfRecivedPicture;      // 接收的图片文件
 | 
			
		||||
    private File mfTempCropPicture;     // 剪裁临时文件
 | 
			
		||||
    private File mfRecivedCropPicture;  // 剪裁后的目标文件
 | 
			
		||||
 | 
			
		||||
    // 静态变量
 | 
			
		||||
    public static String _mszRecivedCropPicture = "RecivedCrop.jpg";
 | 
			
		||||
    File mfRecivedCropPicture;
 | 
			
		||||
    static String _mszCommonFileType = "jpeg";
 | 
			
		||||
    // 背景图片的压缩比
 | 
			
		||||
    int mnPictureCompress = 100;
 | 
			
		||||
    static String _RecivedPictureFileName;
 | 
			
		||||
    private static String _mszCommonFileType = "jpeg";
 | 
			
		||||
    private int mnPictureCompress = 100;
 | 
			
		||||
    private static String _RecivedPictureFileName;
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Activity getActivity() {
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String getTag() {
 | 
			
		||||
		return TAG;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
@@ -67,30 +75,29 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
        setContentView(R.layout.activity_backgroundpicture);
 | 
			
		||||
        initEnv();
 | 
			
		||||
 | 
			
		||||
        // 初始化工具类和文件夹
 | 
			
		||||
        mBackgroundPictureUtils = BackgroundPictureUtils.getInstance(this);
 | 
			
		||||
        mfBackgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir());
 | 
			
		||||
        if (!mfBackgroundDir.exists()) {
 | 
			
		||||
            mfBackgroundDir.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
        //mfPictureDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), getString(R.string.app_projectname));
 | 
			
		||||
 | 
			
		||||
        mfPictureDir = new File(App.getTempDirPath());
 | 
			
		||||
        if (!mfPictureDir.exists()) {
 | 
			
		||||
            mfPictureDir.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化文件对象
 | 
			
		||||
        mfTakePhoto = new File(mfPictureDir, "TakePhoto.jpg");
 | 
			
		||||
        mfTempCropPicture = new File(mfPictureDir, "TempCrop.jpg");
 | 
			
		||||
 | 
			
		||||
        mfRecivedPicture = getRecivedPictureFile(this);
 | 
			
		||||
        mfRecivedCropPicture = new File(mfBackgroundDir, _mszRecivedCropPicture);
 | 
			
		||||
 | 
			
		||||
        // 初始化工具栏
 | 
			
		||||
        mAToolbar = (AToolbar) findViewById(R.id.toolbar);
 | 
			
		||||
        setActionBar(mAToolbar);
 | 
			
		||||
        //mAToolbar.setTitle(getTitle() + "-" + getString(R.string.subtitle_activity_backgroundpicture));
 | 
			
		||||
        mAToolbar.setSubtitle(R.string.subtitle_activity_backgroundpicture);
 | 
			
		||||
        //mAToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
 | 
			
		||||
        //mAToolbar.setSubtitleTextAppearance(this, R.style.Toolbar_SubTitleText);
 | 
			
		||||
        //mAToolbar.setBackgroundColor(getColor(R.color.colorPrimary));
 | 
			
		||||
        setActionBar(mAToolbar);
 | 
			
		||||
        getActionBar().setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
        mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
@@ -99,41 +106,30 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        //给按钮设置点击事件
 | 
			
		||||
        // 设置按钮点击事件
 | 
			
		||||
        findViewById(R.id.activitybackgroundpictureAButton5).setOnClickListener(onOriginNullClickListener);
 | 
			
		||||
        findViewById(R.id.activitybackgroundpictureAButton4).setOnClickListener(onReceivedPictureClickListener);
 | 
			
		||||
        findViewById(R.id.activitybackgroundpictureAButton1).setOnClickListener(onTakePhotoClickListener);
 | 
			
		||||
        findViewById(R.id.activitybackgroundpictureAButton2).setOnClickListener(onSelectPictureClickListener);
 | 
			
		||||
        findViewById(R.id.activitybackgroundpictureAButton3).setOnClickListener(onCropPictureClickListener);
 | 
			
		||||
        findViewById(R.id.activitybackgroundpictureAButton6).setOnClickListener(onCropFreePictureClickListener);
 | 
			
		||||
		findViewById(R.id.activitybackgroundpictureAButton7).setOnClickListener(onPixelPickerClickListener);
 | 
			
		||||
		findViewById(R.id.activitybackgroundpictureAButton8).setOnClickListener(onCleanPixelClickListener);
 | 
			
		||||
 | 
			
		||||
        updatePreviewBackground();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // 判断并且处理应用分享到的文件
 | 
			
		||||
        //
 | 
			
		||||
        //ToastUtils.show("Activity Opened.");
 | 
			
		||||
 | 
			
		||||
        // 预备接收参数
 | 
			
		||||
        // 处理分享的图片
 | 
			
		||||
        Intent intent = getIntent();
 | 
			
		||||
        String action = intent.getAction();
 | 
			
		||||
        String type = intent.getType();
 | 
			
		||||
 | 
			
		||||
        //LogUtils.d(TAG, "action : " + action);
 | 
			
		||||
        //LogUtils.d(TAG, "type : " + type);
 | 
			
		||||
 | 
			
		||||
        // 判断是否进入图片分享状态
 | 
			
		||||
        if (Intent.ACTION_SEND.equals(action)
 | 
			
		||||
            && type != null
 | 
			
		||||
            && ("image/*".equals(type) || "image/jpeg".equals(type) || "image/jpg".equals(type) || "image/png".equals(type) || "image/webp".equals(type))) {
 | 
			
		||||
            // 预览图片
 | 
			
		||||
            BackgroundPicturePreviewDialog dlg= new BackgroundPicturePreviewDialog(this);
 | 
			
		||||
        if (Intent.ACTION_SEND.equals(action) && type != null && isImageType(type)) {
 | 
			
		||||
            BackgroundPicturePreviewDialog dlg = new BackgroundPicturePreviewDialog(this);
 | 
			
		||||
            dlg.show();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void initEnv() {
 | 
			
		||||
    private void initEnv() {
 | 
			
		||||
        LogUtils.d(TAG, "initEnv()");
 | 
			
		||||
        _RecivedPictureFileName = "Recived.data";
 | 
			
		||||
    }
 | 
			
		||||
@@ -144,47 +140,55 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onAcceptRecivedPicture(String szPreRecivedPictureName) {
 | 
			
		||||
        //ToastUtils.show("onAcceptRecivedPicture");
 | 
			
		||||
        BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this);
 | 
			
		||||
        utils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
 | 
			
		||||
        utils.saveData();
 | 
			
		||||
        File fPreRecivedPictureName = new File(utils.getBackgroundDir(), szPreRecivedPictureName);
 | 
			
		||||
        FileUtils.copyFile(fPreRecivedPictureName, mfRecivedPicture);
 | 
			
		||||
        // 加载背景
 | 
			
		||||
        startCropImageActivity(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 更新预览背景
 | 
			
		||||
    //
 | 
			
		||||
    public void updatePreviewBackground() {
 | 
			
		||||
        LogUtils.d(TAG, "updatePreviewBackground");
 | 
			
		||||
        ImageView ivPreviewBackground = findViewById(R.id.activitybackgroundpictureImageView1);
 | 
			
		||||
        BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this);
 | 
			
		||||
        utils.loadBackgroundPictureBean();
 | 
			
		||||
        boolean isUseBackgroundFile = utils.getBackgroundPictureBean().isUseBackgroundFile();
 | 
			
		||||
        if (isUseBackgroundFile && mfRecivedCropPicture.exists()) {
 | 
			
		||||
            try {
 | 
			
		||||
                String szBackgroundFilePath = utils.getBackgroundDir() + getBackgroundFileName();
 | 
			
		||||
                Drawable drawableBackground = FileUtils.getImageDrawable(szBackgroundFilePath);
 | 
			
		||||
                drawableBackground.setAlpha(120);
 | 
			
		||||
                ivPreviewBackground.setImageDrawable(drawableBackground);
 | 
			
		||||
                ToastUtils.show("Use acceptRecived background.");
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            }
 | 
			
		||||
        File sourceFile = new File(utils.getBackgroundDir(), szPreRecivedPictureName);
 | 
			
		||||
        if (FileUtils.copyFile(sourceFile, mfRecivedPicture)) {
 | 
			
		||||
            startCropImageActivity(false);
 | 
			
		||||
        } else {
 | 
			
		||||
            ToastUtils.show(" No background.");
 | 
			
		||||
            Drawable drawableBackground = getDrawable(R.drawable.blank10x10);
 | 
			
		||||
            drawableBackground.setAlpha(120);
 | 
			
		||||
            ivPreviewBackground.setImageDrawable(drawableBackground);
 | 
			
		||||
            ToastUtils.show("图片复制失败,请重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新背景图片预览
 | 
			
		||||
     */
 | 
			
		||||
    public void updatePreviewBackground() {
 | 
			
		||||
        LogUtils.d(TAG, "updatePreviewBackground");
 | 
			
		||||
        ImageView ivPreviewBackground = (ImageView) findViewById(R.id.activitybackgroundpictureImageView1);
 | 
			
		||||
        BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this);
 | 
			
		||||
        utils.loadBackgroundPictureBean();
 | 
			
		||||
 | 
			
		||||
        boolean isUseBackgroundFile = utils.getBackgroundPictureBean().isUseBackgroundFile();
 | 
			
		||||
        if (isUseBackgroundFile && mfRecivedCropPicture.exists()) {
 | 
			
		||||
            try {
 | 
			
		||||
                String filePath = utils.getBackgroundDir() + getBackgroundFileName();
 | 
			
		||||
                Drawable drawable = FileUtils.getImageDrawable(filePath);
 | 
			
		||||
                if (drawable != null) {
 | 
			
		||||
                    //drawable.setAlpha(120);
 | 
			
		||||
                    ivPreviewBackground.setImageDrawable(drawable);
 | 
			
		||||
                }
 | 
			
		||||
                //ToastUtils.show("背景图片已更新");
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
                ToastUtils.show("背景图片加载失败");
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            ToastUtils.show("未使用背景图片");
 | 
			
		||||
            Drawable drawable = getResources().getDrawable(R.drawable.blank10x10);
 | 
			
		||||
            if (drawable != null) {
 | 
			
		||||
                drawable.setAlpha(120);
 | 
			
		||||
                ivPreviewBackground.setImageDrawable(drawable);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 点击事件监听器
 | 
			
		||||
    private View.OnClickListener onOriginNullClickListener = new View.OnClickListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onClick(View v) {
 | 
			
		||||
            // 选择原始空白背景
 | 
			
		||||
            BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
 | 
			
		||||
            BackgroundPictureBean bean = utils.getBackgroundPictureBean();
 | 
			
		||||
            bean.setIsUseBackgroundFile(false);
 | 
			
		||||
@@ -196,11 +200,10 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
    private View.OnClickListener onSelectPictureClickListener = new View.OnClickListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onClick(View v) {
 | 
			
		||||
            // 导入外部图片
 | 
			
		||||
            Intent intent = new Intent(
 | 
			
		||||
                Intent.ACTION_PICK,
 | 
			
		||||
                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
 | 
			
		||||
            startActivityForResult(intent, REQUEST_SELECT_PICTURE);
 | 
			
		||||
            if (checkAndRequestStoragePermission()) {
 | 
			
		||||
                Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
 | 
			
		||||
                startActivityForResult(intent, REQUEST_SELECT_PICTURE);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -211,7 +214,7 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
            if (fCheck.exists()) {
 | 
			
		||||
                startCropImageActivity(false);
 | 
			
		||||
            } else {
 | 
			
		||||
                ToastUtils.show("There is not any picture to crop.");
 | 
			
		||||
                ToastUtils.show("没有可剪裁的图片");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
@@ -223,7 +226,7 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
            if (fCheck.exists()) {
 | 
			
		||||
                startCropImageActivity(true);
 | 
			
		||||
            } else {
 | 
			
		||||
                ToastUtils.show("There is not any picture to crop.");
 | 
			
		||||
                ToastUtils.show("没有可剪裁的图片");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
@@ -233,6 +236,7 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
        public void onClick(View v) {
 | 
			
		||||
            LogUtils.d(TAG, "onTakePhotoClickListener");
 | 
			
		||||
            LogUtils.d(TAG, "mfTakePhoto : " + mfTakePhoto.getPath());
 | 
			
		||||
 | 
			
		||||
            if (mfTakePhoto.exists()) {
 | 
			
		||||
                mfTakePhoto.delete();
 | 
			
		||||
            }
 | 
			
		||||
@@ -240,56 +244,94 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
                mfTakePhoto.createNewFile();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
                ToastUtils.show("拍照文件创建失败");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (checkAndRequestStoragePermission()) {
 | 
			
		||||
                Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 | 
			
		||||
                startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
 | 
			
		||||
            }
 | 
			
		||||
            Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 | 
			
		||||
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private View.OnClickListener onReceivedPictureClickListener = new View.OnClickListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onClick(View v) {
 | 
			
		||||
            // 选择接收到的背景图片
 | 
			
		||||
            BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
 | 
			
		||||
			utils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
 | 
			
		||||
            utils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
 | 
			
		||||
            utils.saveData();
 | 
			
		||||
            updatePreviewBackground();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
	private View.OnClickListener onPixelPickerClickListener = new View.OnClickListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onClick(View v) {
 | 
			
		||||
			// 从文件路径启动像素拾取活动
 | 
			
		||||
			//String imagePath = "/storage/emulated/0/DCIM/Camera/sample.jpg";
 | 
			
		||||
			String imagePath = mfRecivedCropPicture.toString();
 | 
			
		||||
			Intent intent = new Intent(getApplicationContext(), PixelPickerActivity.class);
 | 
			
		||||
			intent.putExtra("imagePath", imagePath);
 | 
			
		||||
			startActivity(intent);
 | 
			
		||||
            //App.getWinBoLLActivityManager().startWinBoLLActivity(getActivity(), intent, PixelPickerActivity.class);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
	private View.OnClickListener onCleanPixelClickListener = new View.OnClickListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onClick(View v) {
 | 
			
		||||
			BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
 | 
			
		||||
			BackgroundPictureBean bean = utils.getBackgroundPictureBean();
 | 
			
		||||
			bean.setPixelColor(0);
 | 
			
		||||
			utils.saveData();
 | 
			
		||||
			setBackgroundColor();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 压缩图片并保存到接收文件
 | 
			
		||||
     */
 | 
			
		||||
    void compressQualityToRecivedPicture(Bitmap bitmap) {
 | 
			
		||||
        // 设置输出流
 | 
			
		||||
        OutputStream outStream = null;
 | 
			
		||||
        try {
 | 
			
		||||
            // 创建输出流对象,准备写入压缩后的图片文件
 | 
			
		||||
            mfRecivedPicture = getRecivedPictureFile(this);
 | 
			
		||||
            // 创建新的接收文件
 | 
			
		||||
            if (!mfRecivedPicture.exists()) {
 | 
			
		||||
                mfRecivedPicture.createNewFile();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            FileOutputStream fos = new FileOutputStream(mfRecivedPicture);
 | 
			
		||||
 | 
			
		||||
            // 获取输出流对象
 | 
			
		||||
            outStream = new BufferedOutputStream(fos);
 | 
			
		||||
 | 
			
		||||
            // 使用默认的质量参数压缩图片
 | 
			
		||||
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream); // 70% 的质量
 | 
			
		||||
 | 
			
		||||
            // 关闭输出流以完成文件操作
 | 
			
		||||
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
 | 
			
		||||
            outStream.flush();
 | 
			
		||||
            outStream.close();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            ToastUtils.show("图片压缩失败");
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (outStream != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    outStream.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (bitmap != null && !bitmap.isRecycled()) {
 | 
			
		||||
                bitmap.recycle();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 启动图片裁剪活动
 | 
			
		||||
     * @param isCropFree 是否自由裁剪
 | 
			
		||||
     */
 | 
			
		||||
    public void startCropImageActivity(boolean isCropFree) {
 | 
			
		||||
        LogUtils.d(TAG, "startCropImageActivity");
 | 
			
		||||
        BackgroundPictureBean bean = mBackgroundPictureUtils.loadBackgroundPictureBean();
 | 
			
		||||
        mfRecivedPicture = getRecivedPictureFile(this);
 | 
			
		||||
        Uri uri = UriUtil.getUriForFile(this, mfRecivedPicture);
 | 
			
		||||
        LogUtils.d(TAG, "uri : " + uri.toString());
 | 
			
		||||
 | 
			
		||||
        if (mfTempCropPicture.exists()) {
 | 
			
		||||
            mfTempCropPicture.delete();
 | 
			
		||||
        }
 | 
			
		||||
@@ -297,27 +339,24 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
            mfTempCropPicture.createNewFile();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
            ToastUtils.show("剪裁临时文件创建失败");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // 使用正确的文件路径构建 Uri
 | 
			
		||||
 | 
			
		||||
        Uri cropOutPutUri = Uri.fromFile(mfTempCropPicture);
 | 
			
		||||
        LogUtils.d(TAG, "mfTempCropPicture : " + mfTempCropPicture.getPath());
 | 
			
		||||
 | 
			
		||||
        Intent intent = new Intent("com.android.camera.action.CROP");
 | 
			
		||||
        intent.setDataAndType(uri, "image/" + _mszCommonFileType);
 | 
			
		||||
        // 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
 | 
			
		||||
        intent.putExtra("crop", "true");
 | 
			
		||||
        intent.putExtra("noFaceDetection", true);
 | 
			
		||||
 | 
			
		||||
        if (!isCropFree) {
 | 
			
		||||
            // aspectX aspectY 是宽高的比例
 | 
			
		||||
            intent.putExtra("aspectX", bean.getBackgroundWidth());
 | 
			
		||||
            intent.putExtra("aspectY", bean.getBackgroundHeight());
 | 
			
		||||
        }
 | 
			
		||||
        // outputX outputY 是裁剪图片宽高
 | 
			
		||||
        //intent.putExtra("outputX", 100);
 | 
			
		||||
        //intent.putExtra("outputY", 100);
 | 
			
		||||
        //return-data =false 意味着裁剪成功后不能在onActivityResult 的intent 中获得图片
 | 
			
		||||
        //intent.putExtra("return-data", false);
 | 
			
		||||
 | 
			
		||||
        intent.putExtra("return-data", true);
 | 
			
		||||
        //裁剪后的图片输出至  cropOutPutUri
 | 
			
		||||
        intent.putExtra(MediaStore.EXTRA_OUTPUT, cropOutPutUri);
 | 
			
		||||
        intent.putExtra("scale", true);
 | 
			
		||||
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
 | 
			
		||||
@@ -325,13 +364,102 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
        startActivityForResult(intent, REQUEST_CROP_IMAGE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 启动裁剪窗口,裁剪操作文件为 uirImage
 | 
			
		||||
    //
 | 
			
		||||
    /**
 | 
			
		||||
     * 保存剪裁后的Bitmap(优化版)
 | 
			
		||||
     */
 | 
			
		||||
    private void saveCropBitmap(Bitmap bitmap) {
 | 
			
		||||
        if (bitmap == null) {
 | 
			
		||||
            ToastUtils.show("剪裁图片为空");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 内存优化:大图片自动缩放
 | 
			
		||||
        Bitmap scaledBitmap = bitmap;
 | 
			
		||||
        if (bitmap.getByteCount() > 10 * 1024 * 1024) { // 超过10MB
 | 
			
		||||
            float scale = 1.0f;
 | 
			
		||||
            while (scaledBitmap.getByteCount() > 5 * 1024 * 1024) {
 | 
			
		||||
                scale -= 0.2f; // 每次缩小20%
 | 
			
		||||
                if (scale < 0.2f) break; // 最小缩放到20%
 | 
			
		||||
                scaledBitmap = scaleBitmap(scaledBitmap, scale);
 | 
			
		||||
            }
 | 
			
		||||
            if (scaledBitmap != bitmap) {
 | 
			
		||||
                bitmap.recycle(); // 回收原Bitmap
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 优化:创建保存目录
 | 
			
		||||
        File backgroundDir = new File(mBackgroundPictureUtils.getBackgroundDir());
 | 
			
		||||
        if (!backgroundDir.exists()) {
 | 
			
		||||
            if (!backgroundDir.mkdirs()) {
 | 
			
		||||
                ToastUtils.show("无法创建保存目录");
 | 
			
		||||
                if (scaledBitmap != bitmap) scaledBitmap.recycle();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        File saveFile = new File(backgroundDir, getBackgroundFileName());
 | 
			
		||||
 | 
			
		||||
        // 优化:检查文件是否可写
 | 
			
		||||
        if (saveFile.exists() && !saveFile.canWrite()) {
 | 
			
		||||
            if (!saveFile.delete()) {
 | 
			
		||||
                ToastUtils.show("无法删除旧文件");
 | 
			
		||||
                if (scaledBitmap != bitmap) scaledBitmap.recycle();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        FileOutputStream fos = null;
 | 
			
		||||
        try {
 | 
			
		||||
            fos = new FileOutputStream(saveFile);
 | 
			
		||||
            boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
 | 
			
		||||
            fos.flush();
 | 
			
		||||
            if (success) {
 | 
			
		||||
                ToastUtils.show("保存成功");
 | 
			
		||||
                // 更新数据
 | 
			
		||||
                mBackgroundPictureUtils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
 | 
			
		||||
                updatePreviewBackground();
 | 
			
		||||
            } else {
 | 
			
		||||
                ToastUtils.show("图片压缩保存失败");
 | 
			
		||||
            }
 | 
			
		||||
        } catch (FileNotFoundException e) {
 | 
			
		||||
            LogUtils.e(TAG, "文件未找到" + e);
 | 
			
		||||
            ToastUtils.show("保存失败:文件路径错误");
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            LogUtils.e(TAG, "写入异常" + e);
 | 
			
		||||
            ToastUtils.show("保存失败:磁盘可能已满或路径错误");
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (fos != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fos.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    LogUtils.e(TAG, "流关闭异常" + e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
 | 
			
		||||
                scaledBitmap.recycle();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 缩放Bitmap
 | 
			
		||||
     */
 | 
			
		||||
    private Bitmap scaleBitmap(Bitmap original, float scale) {
 | 
			
		||||
        if (original == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        int width = (int) (original.getWidth() * scale);
 | 
			
		||||
        int height = (int) (original.getHeight() * scale);
 | 
			
		||||
        return Bitmap.createScaledBitmap(original, width, height, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 分享图片
 | 
			
		||||
     */
 | 
			
		||||
    void sharePicture() {
 | 
			
		||||
        Uri uri = UriUtil.getUriForFile(this, mfRecivedPicture);
 | 
			
		||||
        Intent shareIntent = new Intent();    
 | 
			
		||||
        shareIntent.setAction(Intent.ACTION_SEND);    
 | 
			
		||||
        shareIntent.putExtra(Intent.EXTRA_STREAM, uri);    
 | 
			
		||||
        Intent shareIntent = new Intent(Intent.ACTION_SEND);
 | 
			
		||||
        shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
 | 
			
		||||
        shareIntent.setType("image/" + _mszCommonFileType);
 | 
			
		||||
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 | 
			
		||||
        startActivity(Intent.createChooser(shareIntent, "Share Image"));
 | 
			
		||||
@@ -345,45 +473,121 @@ implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 | 
			
		||||
 | 
			
		||||
        super.onActivityResult(requestCode, resultCode, data);
 | 
			
		||||
        if (requestCode == REQUEST_SELECT_PICTURE) {
 | 
			
		||||
            // 处理选择后图片
 | 
			
		||||
            if (resultCode == RESULT_OK) {
 | 
			
		||||
                try {
 | 
			
		||||
                    Uri selectedImage = data.getData(); 
 | 
			
		||||
                    LogUtils.d(TAG, "Uri is : " + selectedImage.toString());
 | 
			
		||||
                    File fSrcImage = new File(UriUtil.getFilePathFromUri(this, selectedImage));
 | 
			
		||||
                    mfRecivedPicture = getRecivedPictureFile(this);
 | 
			
		||||
 | 
			
		||||
                    FileUtils.copyFile(fSrcImage, mfRecivedPicture);
 | 
			
		||||
                    // 启动剪裁文件窗口
 | 
			
		||||
        if (requestCode == REQUEST_SELECT_PICTURE && resultCode == RESULT_OK) {
 | 
			
		||||
            try {
 | 
			
		||||
                Uri selectedImage = data.getData();
 | 
			
		||||
                LogUtils.d(TAG, "Uri is : " + selectedImage.toString());
 | 
			
		||||
                File fSrcImage = new File(UriUtil.getFilePathFromUri(this, selectedImage));
 | 
			
		||||
                mfRecivedPicture = getRecivedPictureFile(this);
 | 
			
		||||
                if (FileUtils.copyFile(fSrcImage, mfRecivedPicture)) {
 | 
			
		||||
                    startCropImageActivity(false);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
			
		||||
                } else {
 | 
			
		||||
                    ToastUtils.show("图片复制失败,请重试");
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                LogUtils.e(TAG, "选择图片异常" + e);
 | 
			
		||||
                ToastUtils.show("选择图片失败:" + e.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
        } else if (requestCode == REQUEST_TAKE_PHOTO) {
 | 
			
		||||
            if (resultCode == RESULT_OK) {
 | 
			
		||||
                LogUtils.d(TAG, "REQUEST_TAKE_PHOTO");
 | 
			
		||||
                Bundle extras = data.getExtras();
 | 
			
		||||
        } else if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
 | 
			
		||||
            LogUtils.d(TAG, "REQUEST_TAKE_PHOTO");
 | 
			
		||||
            Bundle extras = data.getExtras();
 | 
			
		||||
            if (extras != null) {
 | 
			
		||||
                Bitmap imageBitmap = (Bitmap) extras.get("data");
 | 
			
		||||
                compressQualityToRecivedPicture(imageBitmap);
 | 
			
		||||
                startCropImageActivity(false);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (requestCode == REQUEST_CROP_IMAGE) {
 | 
			
		||||
            if (resultCode == RESULT_OK) {
 | 
			
		||||
                LogUtils.d(TAG, "CROP_IMAGE_REQUEST_CODE");
 | 
			
		||||
                FileUtils.copyFile(mfTempCropPicture, mfRecivedCropPicture);
 | 
			
		||||
                mfTempCropPicture.delete();
 | 
			
		||||
                mBackgroundPictureUtils.getBackgroundPictureBean().setIsUseBackgroundFile(true);
 | 
			
		||||
                updatePreviewBackground();
 | 
			
		||||
                if (imageBitmap != null) {
 | 
			
		||||
                    compressQualityToRecivedPicture(imageBitmap);
 | 
			
		||||
                    startCropImageActivity(false);
 | 
			
		||||
                } else {
 | 
			
		||||
                    ToastUtils.show("拍照图片为空");
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ToastUtils.show("拍照数据获取失败");
 | 
			
		||||
            }
 | 
			
		||||
        } else if (requestCode == REQUEST_CROP_IMAGE && resultCode == RESULT_OK) {
 | 
			
		||||
            LogUtils.d(TAG, "CROP_IMAGE_REQUEST_CODE");
 | 
			
		||||
            try {
 | 
			
		||||
                Bitmap cropBitmap = null;
 | 
			
		||||
                // 方案1:通过Intent获取剪裁后的Bitmap
 | 
			
		||||
                if (data != null && data.hasExtra("data")) {
 | 
			
		||||
                    cropBitmap = data.getParcelableExtra("data");
 | 
			
		||||
                } else if (mfTempCropPicture.exists()) {
 | 
			
		||||
                    cropBitmap = BitmapFactory.decodeFile(mfTempCropPicture.getPath());
 | 
			
		||||
                } else {
 | 
			
		||||
                    ToastUtils.show("剪裁文件不存在");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            String sz = "Unsolved requestCode = " + Integer.toString(requestCode);
 | 
			
		||||
            Toast.makeText(getApplication(), sz, Toast.LENGTH_SHORT).show();
 | 
			
		||||
            LogUtils.d(TAG, sz);
 | 
			
		||||
                if (cropBitmap != null) {
 | 
			
		||||
                    saveCropBitmap(cropBitmap);
 | 
			
		||||
                } else {
 | 
			
		||||
                    ToastUtils.show("获取剪裁图片失败");
 | 
			
		||||
                }
 | 
			
		||||
            } catch (OutOfMemoryError e) {
 | 
			
		||||
                LogUtils.e(TAG, "内存溢出" + e);
 | 
			
		||||
                ToastUtils.show("保存失败:内存不足,请尝试裁剪更小的图片");
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                LogUtils.e(TAG, "剪裁保存异常" + e);
 | 
			
		||||
                ToastUtils.show("保存失败:" + e.getMessage());
 | 
			
		||||
            }/* finally {
 | 
			
		||||
			 // 安全删除临时文件
 | 
			
		||||
			 if (mfTempCropPicture.exists()) {
 | 
			
		||||
			 mfTempCropPicture.delete();
 | 
			
		||||
			 }
 | 
			
		||||
			 }*/
 | 
			
		||||
        } else if (resultCode != RESULT_OK) {
 | 
			
		||||
            LogUtils.d(TAG, "操作取消或失败,requestCode: " + requestCode);
 | 
			
		||||
            ToastUtils.show("操作已取消");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查类型是否为图片
 | 
			
		||||
     */
 | 
			
		||||
    private boolean isImageType(String type) {
 | 
			
		||||
        return type.startsWith("image/") || "image/jpeg".equals(type) || 
 | 
			
		||||
            "image/jpg".equals(type) || "image/png".equals(type) || 
 | 
			
		||||
            "image/webp".equals(type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查并申请存储权限
 | 
			
		||||
     */
 | 
			
		||||
    private boolean checkAndRequestStoragePermission() {
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
			
		||||
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
 | 
			
		||||
                ActivityCompat.requestPermissions(this, 
 | 
			
		||||
                                                  new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 
 | 
			
		||||
                                                  STORAGE_PERMISSION_REQUEST);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
 | 
			
		||||
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 | 
			
		||||
        if (requestCode == STORAGE_PERMISSION_REQUEST) {
 | 
			
		||||
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 | 
			
		||||
                ToastUtils.show("存储权限已获取");
 | 
			
		||||
            } else {
 | 
			
		||||
                ToastUtils.show("需要存储权限才能保存图片");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	void setBackgroundColor() {
 | 
			
		||||
		BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(BackgroundPictureActivity.this);
 | 
			
		||||
		BackgroundPictureBean bean = utils.getBackgroundPictureBean();
 | 
			
		||||
		int nPixelColor = bean.getPixelColor();
 | 
			
		||||
		RelativeLayout mainLayout = findViewById(R.id.activitybackgroundpictureRelativeLayout1);
 | 
			
		||||
		mainLayout.setBackgroundColor(nPixelColor);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void onResume() {
 | 
			
		||||
		super.onResume();
 | 
			
		||||
		setBackgroundColor();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,251 @@
 | 
			
		||||
package cc.winboll.studio.powerbell.activities;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/22 14:15
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.Dialog;
 | 
			
		||||
import android.graphics.Bitmap;
 | 
			
		||||
import android.graphics.BitmapFactory;
 | 
			
		||||
import android.graphics.Color;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import android.view.MotionEvent;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.RelativeLayout;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import cc.winboll.studio.libaes.views.AToolbar;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.MainActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.R;
 | 
			
		||||
import cc.winboll.studio.powerbell.activities.PixelPickerActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
 | 
			
		||||
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
 | 
			
		||||
public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
	public static final String TAG = "PixelPickerActivity";
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Activity getActivity() {
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String getTag() {
 | 
			
		||||
		return TAG;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private AToolbar mAToolbar;
 | 
			
		||||
	private ImageView imageView;
 | 
			
		||||
	private Bitmap originalBitmap;
 | 
			
		||||
	private TextView infoText;
 | 
			
		||||
	private ViewGroup imageContainer;
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
		super.onCreate(savedInstanceState);
 | 
			
		||||
		setContentView(R.layout.activity_pixelpicker);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // 初始化工具栏
 | 
			
		||||
        mAToolbar = (AToolbar) findViewById(R.id.toolbar);
 | 
			
		||||
        setActionBar(mAToolbar);
 | 
			
		||||
        mAToolbar.setSubtitle(R.string.subtitle_activity_pixelpicker);
 | 
			
		||||
        getActionBar().setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
        mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onClick(View v) {
 | 
			
		||||
                    finish();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
		imageView = findViewById(R.id.imageView);
 | 
			
		||||
		infoText = findViewById(R.id.infoText);
 | 
			
		||||
		imageContainer = findViewById(R.id.imageContainer);
 | 
			
		||||
 | 
			
		||||
		// 从Intent获取图片路径并加载
 | 
			
		||||
		String imagePath = getIntent().getStringExtra("imagePath");
 | 
			
		||||
		if (imagePath != null) {
 | 
			
		||||
			loadImage(imagePath);
 | 
			
		||||
		} else {
 | 
			
		||||
			infoText.setText("未找到图片路径");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 设置图片点击事件
 | 
			
		||||
		imageContainer.setOnTouchListener(new View.OnTouchListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public boolean onTouch(View v, MotionEvent event) {
 | 
			
		||||
					if (event.getAction() == MotionEvent.ACTION_DOWN && originalBitmap != null) {
 | 
			
		||||
						// 计算点击位置在图片上的实际坐标
 | 
			
		||||
						float touchX = event.getX();
 | 
			
		||||
						float touchY = event.getY();
 | 
			
		||||
 | 
			
		||||
						int pixelX = -1, pixelY = -1;
 | 
			
		||||
						try {
 | 
			
		||||
							// 获取图片在容器中的实际位置和尺寸
 | 
			
		||||
							int[] imageLocation = new int[2];
 | 
			
		||||
							imageView.getLocationInWindow(imageLocation);
 | 
			
		||||
							int imageWidth = imageView.getWidth();
 | 
			
		||||
							int imageHeight = imageView.getHeight();
 | 
			
		||||
 | 
			
		||||
							// 计算缩放比例
 | 
			
		||||
							float scaleX = (float) originalBitmap.getWidth() / imageWidth;
 | 
			
		||||
							float scaleY = (float) originalBitmap.getHeight() / imageHeight;
 | 
			
		||||
 | 
			
		||||
							// 调整触摸坐标到图片坐标系
 | 
			
		||||
							float adjustedX = touchX - imageLocation[0];
 | 
			
		||||
							float adjustedY = touchY - imageLocation[1];
 | 
			
		||||
 | 
			
		||||
							// 检查是否在图片范围内
 | 
			
		||||
							if (adjustedX >= 0 && adjustedX <= imageWidth && adjustedY >= 0 && adjustedY <= imageHeight) {
 | 
			
		||||
								// 计算实际像素坐标
 | 
			
		||||
								pixelX = (int) (adjustedX * scaleX);
 | 
			
		||||
								pixelY = (int) (adjustedY * scaleY);
 | 
			
		||||
 | 
			
		||||
								// 再次检查像素坐标是否在有效范围内
 | 
			
		||||
								if (pixelX >= 0 && pixelX < originalBitmap.getWidth() && 
 | 
			
		||||
									pixelY >= 0 && pixelY < originalBitmap.getHeight()) {
 | 
			
		||||
									int pixelColor = originalBitmap.getPixel(pixelX, pixelY);
 | 
			
		||||
									showPixelDialog(pixelColor, pixelX, pixelY);
 | 
			
		||||
								} else {
 | 
			
		||||
									Toast.makeText(PixelPickerActivity.this, "像素坐标超出范围", Toast.LENGTH_SHORT).show();
 | 
			
		||||
								}
 | 
			
		||||
							} else {
 | 
			
		||||
								Toast.makeText(PixelPickerActivity.this, "点击位置超出图片显示范围", Toast.LENGTH_SHORT).show();
 | 
			
		||||
							}
 | 
			
		||||
						} catch (Exception e) {
 | 
			
		||||
							e.printStackTrace();
 | 
			
		||||
							Toast.makeText(PixelPickerActivity.this, "计算像素位置失败", Toast.LENGTH_SHORT).show();
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 加载图片
 | 
			
		||||
	 */
 | 
			
		||||
	private void loadImage(String imagePath) {
 | 
			
		||||
		try {
 | 
			
		||||
			File file = new File(imagePath);
 | 
			
		||||
			if (file.exists()) {
 | 
			
		||||
				// 解码图片
 | 
			
		||||
				BitmapFactory.Options options = new BitmapFactory.Options();
 | 
			
		||||
				options.inSampleSize = 1; // 加载原图
 | 
			
		||||
				originalBitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, options);
 | 
			
		||||
 | 
			
		||||
				if (originalBitmap != null) {
 | 
			
		||||
					imageView.setImageBitmap(originalBitmap);
 | 
			
		||||
					infoText.setText("图片已加载,点击获取像素值");
 | 
			
		||||
				} else {
 | 
			
		||||
					infoText.setText("图片加载失败");
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				infoText.setText("图片文件不存在");
 | 
			
		||||
			}
 | 
			
		||||
		} catch (FileNotFoundException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
			infoText.setText("图片文件未找到");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 显示像素对话框
 | 
			
		||||
	 */
 | 
			
		||||
	private void showPixelDialog(final int pixelColor, int x, int y) {
 | 
			
		||||
		final Dialog dialog = new Dialog(this);
 | 
			
		||||
		dialog.setContentView(R.layout.dialog_pixel);
 | 
			
		||||
		dialog.setCancelable(true);
 | 
			
		||||
 | 
			
		||||
		// 设置像素颜色视图背景
 | 
			
		||||
		TextView colorView = dialog.findViewById(R.id.pixelColorView);
 | 
			
		||||
		colorView.setBackgroundColor(pixelColor);
 | 
			
		||||
 | 
			
		||||
		// 显示颜色信息
 | 
			
		||||
		TextView infoText = dialog.findViewById(R.id.colorInfoText);
 | 
			
		||||
		String colorInfo = String.format(
 | 
			
		||||
			"RGB: (%d, %d, %d)\n" +
 | 
			
		||||
			"ARGB: #%08X\n" +
 | 
			
		||||
			"实际像素位置: (%d, %d)",
 | 
			
		||||
			Color.red(pixelColor),
 | 
			
		||||
			Color.green(pixelColor),
 | 
			
		||||
			Color.blue(pixelColor),
 | 
			
		||||
			pixelColor,
 | 
			
		||||
			x, y);
 | 
			
		||||
		infoText.setText(colorInfo);
 | 
			
		||||
 | 
			
		||||
		// 设置确定按钮点击事件
 | 
			
		||||
		Button confirmButton = dialog.findViewById(R.id.confirmButton);
 | 
			
		||||
		confirmButton.setOnClickListener(new View.OnClickListener() {
 | 
			
		||||
				@Override
 | 
			
		||||
				public void onClick(View v) {
 | 
			
		||||
					dialog.dismiss();
 | 
			
		||||
					// 可以在这里添加确定后的回调逻辑
 | 
			
		||||
					BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(PixelPickerActivity.this);
 | 
			
		||||
					BackgroundPictureBean bean = utils.getBackgroundPictureBean();
 | 
			
		||||
					bean.setPixelColor(pixelColor);
 | 
			
		||||
					utils.saveData();
 | 
			
		||||
					Toast.makeText(PixelPickerActivity.this, "已记录像素值", Toast.LENGTH_SHORT).show();
 | 
			
		||||
					setBackgroundColor();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
		dialog.show();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void onDestroy() {
 | 
			
		||||
		super.onDestroy();
 | 
			
		||||
		// 回收Bitmap资源
 | 
			
		||||
		if (originalBitmap != null && !originalBitmap.isRecycled()) {
 | 
			
		||||
			originalBitmap.recycle();
 | 
			
		||||
			originalBitmap = null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	void setBackgroundColor() {
 | 
			
		||||
		BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(PixelPickerActivity.this);
 | 
			
		||||
		BackgroundPictureBean bean = utils.getBackgroundPictureBean();
 | 
			
		||||
		int nPixelColor = bean.getPixelColor();
 | 
			
		||||
		RelativeLayout mainLayout = findViewById(R.id.activitypixelpickerRelativeLayout1);
 | 
			
		||||
		mainLayout.setBackgroundColor(nPixelColor);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void onResume() {
 | 
			
		||||
		super.onResume();
 | 
			
		||||
		setBackgroundColor();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if (item.getItemId() == android.R.id.home) {
 | 
			
		||||
            GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void onBackPressed() {
 | 
			
		||||
		super.onBackPressed();
 | 
			
		||||
		GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
package cc.winboll.studio.powerbell.activities;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/06/19 20:35
 | 
			
		||||
 * @Describe 应用窗口基类
 | 
			
		||||
 */
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import cc.winboll.studio.libappbase.GlobalApplication;
 | 
			
		||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.App;
 | 
			
		||||
import cc.winboll.studio.powerbell.MainActivity;
 | 
			
		||||
import cc.winboll.studio.powerbell.R;
 | 
			
		||||
 | 
			
		||||
public abstract class WinBoLLActivity extends Activity implements IWinBoLLActivity {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "WinBoLLActivity";
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onPostCreate(savedInstanceState);
 | 
			
		||||
        GlobalApplication.getWinBoLLActivityManager().add(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        GlobalApplication.getWinBoLLActivityManager().registeRemove(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onOptionsItemSelected(MenuItem item) {
 | 
			
		||||
        if (item.getItemId() == android.R.id.home) {
 | 
			
		||||
            GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
 | 
			
		||||
        return super.onOptionsItemSelected(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void onBackPressed() {
 | 
			
		||||
		super.onBackPressed();
 | 
			
		||||
		GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -17,6 +17,8 @@ public class BackgroundPictureBean extends BaseBean {
 | 
			
		||||
    int backgroundWidth = 100;
 | 
			
		||||
    int backgroundHeight = 100;
 | 
			
		||||
    boolean isUseBackgroundFile = false;
 | 
			
		||||
	// 图片拾取像素颜色
 | 
			
		||||
	int pixelColor = 0;
 | 
			
		||||
 | 
			
		||||
    public BackgroundPictureBean() {
 | 
			
		||||
    }
 | 
			
		||||
@@ -25,6 +27,14 @@ public class BackgroundPictureBean extends BaseBean {
 | 
			
		||||
        this.isUseBackgroundFile = isUseBackgroundFile;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	public void setPixelColor(int pixelColor) {
 | 
			
		||||
		this.pixelColor = pixelColor;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public int getPixelColor() {
 | 
			
		||||
		return pixelColor;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    public void setBackgroundWidth(int backgroundWidth) {
 | 
			
		||||
        this.backgroundWidth = backgroundWidth;
 | 
			
		||||
    }
 | 
			
		||||
@@ -61,6 +71,7 @@ public class BackgroundPictureBean extends BaseBean {
 | 
			
		||||
        jsonWriter.name("backgroundWidth").value(bean.getBackgroundWidth());
 | 
			
		||||
        jsonWriter.name("backgroundHeight").value(bean.getBackgroundHeight());
 | 
			
		||||
        jsonWriter.name("isUseBackgroundFile").value(bean.isUseBackgroundFile());
 | 
			
		||||
		jsonWriter.name("pixelColor").value(bean.getPixelColor());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -75,6 +86,8 @@ public class BackgroundPictureBean extends BaseBean {
 | 
			
		||||
                bean.setBackgroundHeight(jsonReader.nextInt());
 | 
			
		||||
            } else if (name.equals("isUseBackgroundFile")) {
 | 
			
		||||
                bean.setIsUseBackgroundFile(jsonReader.nextBoolean());
 | 
			
		||||
            } else if (name.equals("pixelColor")) {
 | 
			
		||||
                bean.setPixelColor(jsonReader.nextInt());
 | 
			
		||||
            } else {
 | 
			
		||||
                jsonReader.skipValue();
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -311,11 +311,11 @@ public class MainViewFragment extends Fragment {
 | 
			
		||||
        LogUtils.d(TAG, String.format("fBackgroundFilePath.exists() %s", fBackgroundFilePath.exists()));
 | 
			
		||||
        if (bean.isUseBackgroundFile() && fBackgroundFilePath.exists()) {
 | 
			
		||||
            Drawable drawableBackground = Drawable.createFromPath(szBackgroundFilePath);
 | 
			
		||||
            drawableBackground.setAlpha(120);
 | 
			
		||||
            //drawableBackground.setAlpha(120);
 | 
			
		||||
            imageView.setImageDrawable(drawableBackground);
 | 
			
		||||
        } else {
 | 
			
		||||
            Drawable drawableBackground = getActivity().getDrawable(R.drawable.blank10x10);
 | 
			
		||||
            drawableBackground.setAlpha(120);
 | 
			
		||||
            //drawableBackground.setAlpha(120);
 | 
			
		||||
            imageView.setImageDrawable(drawableBackground);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<RelativeLayout
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
@@ -12,83 +12,110 @@
 | 
			
		||||
		android:id="@+id/toolbar"
 | 
			
		||||
		style="@style/DefaultAToolbar"/>
 | 
			
		||||
 | 
			
		||||
	<ImageView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="match_parent"
 | 
			
		||||
		android:id="@+id/activitybackgroundpictureImageView1"
 | 
			
		||||
		android:layout_below="@id/toolbar">
 | 
			
		||||
 | 
			
		||||
	</ImageView>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:layout_below="@id/toolbar">
 | 
			
		||||
	<RelativeLayout
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
		<RelativeLayout
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content">
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/activitybackgroundpictureRelativeLayout1"/>
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
				android:layout_width="160dp"
 | 
			
		||||
				android:layout_height="36dp"
 | 
			
		||||
				android:text="Origin BG"
 | 
			
		||||
				android:id="@+id/activitybackgroundpictureAButton5"
 | 
			
		||||
				android:layout_alignParentLeft="true"
 | 
			
		||||
				android:layout_margin="5dp"/>
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
				android:layout_width="160dp"
 | 
			
		||||
				android:layout_height="36dp"
 | 
			
		||||
				android:text="Received BG"
 | 
			
		||||
				android:id="@+id/activitybackgroundpictureAButton4"
 | 
			
		||||
				android:layout_alignParentRight="true"
 | 
			
		||||
				android:layout_margin="5dp"/>
 | 
			
		||||
		<ImageView
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/activitybackgroundpictureImageView1"
 | 
			
		||||
			android:layout_below="@id/toolbar">
 | 
			
		||||
 | 
			
		||||
		</RelativeLayout>
 | 
			
		||||
		</ImageView>
 | 
			
		||||
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			android:orientation="horizontal"
 | 
			
		||||
			android:orientation="vertical"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:gravity="right">
 | 
			
		||||
			android:layout_below="@id/toolbar">
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
				android:layout_width="50dp"
 | 
			
		||||
				android:layout_height="36dp"
 | 
			
		||||
				android:text="◎"
 | 
			
		||||
				android:layout_gravity="center_vertical"
 | 
			
		||||
				android:layout_margin="10dp"
 | 
			
		||||
				android:id="@+id/activitybackgroundpictureAButton1"/>
 | 
			
		||||
			<RelativeLayout
 | 
			
		||||
				android:layout_width="wrap_content"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
				android:layout_width="50dp"
 | 
			
		||||
				android:layout_height="36dp"
 | 
			
		||||
				android:text="☑"
 | 
			
		||||
				android:layout_gravity="center_vertical"
 | 
			
		||||
				android:layout_margin="10dp"
 | 
			
		||||
				android:id="@+id/activitybackgroundpictureAButton2"/>
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="160dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="Origin BG"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton5"
 | 
			
		||||
					android:layout_alignParentLeft="true"
 | 
			
		||||
					android:layout_margin="5dp"/>
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
				android:layout_width="50dp"
 | 
			
		||||
				android:layout_height="36dp"
 | 
			
		||||
				android:text="[+]"
 | 
			
		||||
				android:layout_gravity="center_vertical"
 | 
			
		||||
				android:layout_margin="10dp"
 | 
			
		||||
				android:id="@+id/activitybackgroundpictureAButton3"/>
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="160dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="Received BG"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton4"
 | 
			
		||||
					android:layout_alignParentRight="true"
 | 
			
		||||
					android:layout_margin="5dp"/>
 | 
			
		||||
 | 
			
		||||
			<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
				android:layout_width="50dp"
 | 
			
		||||
				android:layout_height="36dp"
 | 
			
		||||
				android:text="[+~]"
 | 
			
		||||
				android:layout_gravity="center_vertical"
 | 
			
		||||
				android:layout_margin="10dp"
 | 
			
		||||
				android:id="@+id/activitybackgroundpictureAButton6"/>
 | 
			
		||||
			</RelativeLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="right">
 | 
			
		||||
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="50dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="◎"
 | 
			
		||||
					android:layout_gravity="center_vertical"
 | 
			
		||||
					android:layout_margin="5dp"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton1"/>
 | 
			
		||||
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="50dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="☑"
 | 
			
		||||
					android:layout_gravity="center_vertical"
 | 
			
		||||
					android:layout_margin="5dp"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton2"/>
 | 
			
		||||
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="50dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="[+]"
 | 
			
		||||
					android:layout_gravity="center_vertical"
 | 
			
		||||
					android:layout_margin="5dp"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton3"/>
 | 
			
		||||
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="50dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="[+~]"
 | 
			
		||||
					android:layout_gravity="center_vertical"
 | 
			
		||||
					android:layout_margin="5dp"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton6"/>
 | 
			
		||||
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="50dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="[◐]"
 | 
			
		||||
					android:layout_gravity="center_vertical"
 | 
			
		||||
					android:layout_margin="5dp"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton7"/>
 | 
			
		||||
				
 | 
			
		||||
				<cc.winboll.studio.libaes.views.AButton
 | 
			
		||||
					android:layout_width="50dp"
 | 
			
		||||
					android:layout_height="36dp"
 | 
			
		||||
					android:text="[○]"
 | 
			
		||||
					android:layout_gravity="center_vertical"
 | 
			
		||||
					android:layout_margin="5dp"
 | 
			
		||||
					android:id="@+id/activitybackgroundpictureAButton8"/>
 | 
			
		||||
				
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</RelativeLayout>
 | 
			
		||||
	</RelativeLayout>
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,12 +25,19 @@
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<TextView
 | 
			
		||||
		android:layout_width="wrap_content"
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:id="@+id/activityclearrecordTextView1"
 | 
			
		||||
		android:gravity="center_horizontal"
 | 
			
		||||
		android:layout_gravity="center_horizontal"/>
 | 
			
		||||
		android:gravity="center_horizontal">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/activityclearrecordTextView1"
 | 
			
		||||
			android:textAppearance="?android:attr/textAppearanceSmall"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
 
 | 
			
		||||
@@ -2,42 +2,34 @@
 | 
			
		||||
<LinearLayout
 | 
			
		||||
	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_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent"
 | 
			
		||||
	android:orientation="vertical">
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
	<cc.winboll.studio.libaes.views.AToolbar
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
		android:layout_height="@dimen/toolbar_height"
 | 
			
		||||
		android:id="@+id/toolbar"
 | 
			
		||||
		android:gravity="center_vertical"
 | 
			
		||||
		style="@style/DefaultAToolbar"/>
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.libaes.views.AToolbar
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="@dimen/toolbar_height"
 | 
			
		||||
			android:id="@+id/toolbar"
 | 
			
		||||
			android:gravity="center_vertical"
 | 
			
		||||
			style="@style/DefaultAToolbar"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
	<RelativeLayout
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<RelativeLayout
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/activitymainRelativeLayout1"
 | 
			
		||||
			android:background="#FFEE2121"/>
 | 
			
		||||
 | 
			
		||||
		<FrameLayout
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/activitymainFrameLayout1"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="200dp"
 | 
			
		||||
		android:id="@+id/logview"/>
 | 
			
		||||
	</RelativeLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								powerbell/src/main/res/layout/activity_pixelpicker.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								powerbell/src/main/res/layout/activity_pixelpicker.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
<?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:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent"
 | 
			
		||||
	android:orientation="vertical">
 | 
			
		||||
 | 
			
		||||
	<cc.winboll.studio.libaes.views.AToolbar
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="@dimen/toolbar_height"
 | 
			
		||||
		android:id="@+id/toolbar"
 | 
			
		||||
		android:gravity="center_vertical"
 | 
			
		||||
		style="@style/DefaultAToolbar"/>
 | 
			
		||||
 | 
			
		||||
	<RelativeLayout
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<RelativeLayout
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:background="#FFEE2121"
 | 
			
		||||
			android:id="@+id/activitypixelpickerRelativeLayout1"/>
 | 
			
		||||
 | 
			
		||||
		<FrameLayout
 | 
			
		||||
			android:id="@+id/imageContainer"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
			<ImageView
 | 
			
		||||
				android:id="@+id/imageView"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="match_parent"
 | 
			
		||||
				android:src="@drawable/ic_launcher"/>
 | 
			
		||||
 | 
			
		||||
		</FrameLayout>
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:id="@+id/infoText"
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:layout_alignParentBottom="true"
 | 
			
		||||
			android:layout_centerHorizontal="true"
 | 
			
		||||
			android:padding="10dp"
 | 
			
		||||
			android:background="#80000000"
 | 
			
		||||
			android:textColor="#FFFFFF"
 | 
			
		||||
			android:text="点击图片获取像素值"/>
 | 
			
		||||
 | 
			
		||||
	</RelativeLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										32
									
								
								powerbell/src/main/res/layout/dialog_pixel.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								powerbell/src/main/res/layout/dialog_pixel.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:layout_width="240dp"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:orientation="vertical"
 | 
			
		||||
    android:padding="16dp">
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
        android:id="@+id/pixelColorView"
 | 
			
		||||
        android:layout_width="200dp"
 | 
			
		||||
        android:layout_height="200dp"
 | 
			
		||||
        android:layout_gravity="center_horizontal" />
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
        android:id="@+id/colorInfoText"
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_gravity="center_horizontal"
 | 
			
		||||
        android:layout_marginTop="8dp"
 | 
			
		||||
        android:textColor="#333333"
 | 
			
		||||
        android:textSize="14sp" />
 | 
			
		||||
 | 
			
		||||
    <Button
 | 
			
		||||
        android:id="@+id/confirmButton"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_marginTop="16dp"
 | 
			
		||||
        android:background="@android:color/holo_blue_light"
 | 
			
		||||
        android:text="确定"
 | 
			
		||||
        android:textColor="@android:color/white" />
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
@@ -43,15 +43,6 @@
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<TextView
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:text="Tips"
 | 
			
		||||
				android:textSize="@dimen/text_content_size"
 | 
			
		||||
				android:id="@+id/fragmentandroidviewTextView1"
 | 
			
		||||
				android:background="@drawable/bg_frame"
 | 
			
		||||
				android:padding="10dp"/>
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
@@ -205,6 +196,21 @@
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:layout_width="match_parent">
 | 
			
		||||
 | 
			
		||||
			<TextView
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:text="Tips"
 | 
			
		||||
				android:textSize="@dimen/text_content_size"
 | 
			
		||||
				android:id="@+id/fragmentandroidviewTextView1"
 | 
			
		||||
				android:background="@drawable/bg_frame"
 | 
			
		||||
				android:padding="10dp"/>
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</RelativeLayout>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,9 @@
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/action_changepicture"
 | 
			
		||||
        android:title="@string/item_changepicture"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/action_log"
 | 
			
		||||
        android:title="@string/item_logview"/>
 | 
			
		||||
    <item
 | 
			
		||||
        android:id="@+id/action_about"
 | 
			
		||||
        android:title="@string/item_aboutview"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
    <string name="texthint_CustomSlideToCleanRecord">Slide Right To Clean Up APP Record.</string>
 | 
			
		||||
    <string name="subtitle_activity_clearrecord">清理记录</string>
 | 
			
		||||
    <string name="subtitle_activity_backgroundpicture">更换背景图片</string>
 | 
			
		||||
    <string name="subtitle_activity_pixelpicker">背景像素拾取</string>
 | 
			
		||||
    <string name="subtitle_activity_about">关于应用</string>
 | 
			
		||||
    <string name="msg_AOHPCTCSeekBar_ClearRecord">>>>Seek 100% To Clear Battery Record.>>></string>
 | 
			
		||||
	<string name="msg_AOHPCTCSeekBar_ClearRecord">>>>向右滑动100%可以清理电量记录。>>></string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
    <string name="texthint_CustomSlideToCleanRecord">Slide Right To Clean Up APP Record.</string>
 | 
			
		||||
    <string name="subtitle_activity_clearrecord">Clean Record</string>
 | 
			
		||||
    <string name="subtitle_activity_backgroundpicture">Background Picture</string>
 | 
			
		||||
    <string name="subtitle_activity_pixelpicker">Pixel Picker</string>
 | 
			
		||||
    <string name="subtitle_activity_about">About The APP</string>
 | 
			
		||||
    <string name="msg_AOHPCTCSeekBar_ClearRecord">>>>Seek 100% To Clear Battery Record.>>></string>
 | 
			
		||||
    <string name="msg_AOHPCTCSeekBar_ClearRecord">>>>Seek 100% Right Is Clean Record.>>></string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#Created by .winboll/winboll_app_build.gradle
 | 
			
		||||
#Wed May 07 15:07:02 HKT 2025
 | 
			
		||||
stageCount=2
 | 
			
		||||
#Mon Jul 28 11:36:51 HKT 2025
 | 
			
		||||
stageCount=4
 | 
			
		||||
libraryProject=
 | 
			
		||||
baseVersion=15.1
 | 
			
		||||
publishVersion=15.1.1
 | 
			
		||||
publishVersion=15.1.3
 | 
			
		||||
buildCount=0
 | 
			
		||||
baseBetaVersion=15.1.2
 | 
			
		||||
baseBetaVersion=15.1.4
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,9 @@
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="cc.winboll.studio.timestamp">
 | 
			
		||||
 | 
			
		||||
    <!-- 开机启动 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
 | 
			
		||||
 | 
			
		||||
    <!-- 运行前台服务 -->
 | 
			
		||||
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 | 
			
		||||
 | 
			
		||||
@@ -45,6 +48,20 @@
 | 
			
		||||
 | 
			
		||||
        <service android:name=".AssistantService"/>
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".receivers.MainReceiver"
 | 
			
		||||
            android:enabled="true"
 | 
			
		||||
            android:exported="false"
 | 
			
		||||
            android:directBootAware="true">
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
 | 
			
		||||
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
 | 
			
		||||
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import android.view.View;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.Switch;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity;
 | 
			
		||||
import androidx.appcompat.widget.Toolbar;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
@@ -12,6 +13,8 @@ import cc.winboll.studio.libappbase.LogView;
 | 
			
		||||
import cc.winboll.studio.timestamp.MainService;
 | 
			
		||||
import cc.winboll.studio.timestamp.R;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.ClipboardUtil;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.TimeStampUtil;
 | 
			
		||||
import com.hjq.toast.ToastUtils;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
@@ -97,4 +100,22 @@ public class MainActivity extends AppCompatActivity {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	public void onTimeStamp(View view) {
 | 
			
		||||
		// 在这里编写按钮点击后要执行的代码
 | 
			
		||||
		TimeStampUtil.getInstance(this).genTimeStamp();
 | 
			
		||||
		String formattedDateTime = TimeStampUtil.getInstance(this).getTimeStampCopyString();
 | 
			
		||||
		ClipboardUtil.copyTextToClipboard(this, formattedDateTime);
 | 
			
		||||
		
 | 
			
		||||
		// 更新时间戳文本框显示内容
 | 
			
		||||
		EditText etTimeStamp = findViewById(R.id.et_timestamp);
 | 
			
		||||
		etTimeStamp.setText(formattedDateTime);
 | 
			
		||||
 | 
			
		||||
		// 输出一个提示音
 | 
			
		||||
		TimeStampUtil.getInstance(this).playNotifyMusic(this);
 | 
			
		||||
		
 | 
			
		||||
		// 比如显示一个 Toast
 | 
			
		||||
		Toast.makeText(this, "时间戳:\n" + TimeStampUtil.getInstance(this).getTimeStampCopyString() + "\n已拷贝到剪贴板。", Toast.LENGTH_SHORT).show();
 | 
			
		||||
		MainService.updateCopiedTimeStamp();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
package cc.winboll.studio.timestamp.receivers;
 | 
			
		||||
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
			
		||||
import cc.winboll.studio.timestamp.MainService;
 | 
			
		||||
import cc.winboll.studio.timestamp.models.AppConfigsModel;
 | 
			
		||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@188.com>
 | 
			
		||||
 * @Date 2025/07/28 11:15
 | 
			
		||||
 * @Describe MainReceiver
 | 
			
		||||
 */
 | 
			
		||||
public class MainReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
    public static final String TAG = "MainReceiver";
 | 
			
		||||
 | 
			
		||||
    static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
 | 
			
		||||
 | 
			
		||||
    AppConfigsUtil mAppConfigsUtil;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onReceive(Context context, Intent intent) {
 | 
			
		||||
        String szAction = intent.getAction();
 | 
			
		||||
        if (szAction.equals(ACTION_BOOT_COMPLETED)) {
 | 
			
		||||
            AppConfigsModel appConfigs = AppConfigsUtil.getInstance(context).loadAppConfigs();
 | 
			
		||||
			boolean isEnable = appConfigs.isEnableService();
 | 
			
		||||
			AppConfigsUtil.getInstance(context).saveAppConfigs();
 | 
			
		||||
 | 
			
		||||
			Intent intentMainService = new Intent(context, MainService.class);
 | 
			
		||||
			if (isEnable) {
 | 
			
		||||
				context.startService(intentMainService);
 | 
			
		||||
			}
 | 
			
		||||
			LogUtils.i(TAG, "System Boot And Start ManagerService Completed!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -130,16 +130,10 @@ public class NotificationHelper {
 | 
			
		||||
        mForegroundNotification.bigContentView = remoteViews;
 | 
			
		||||
 | 
			
		||||
        service.startForeground(ID_MSG_SERVICE, mForegroundNotification);
 | 
			
		||||
 | 
			
		||||
        // 播放默认短信铃声
 | 
			
		||||
        Uri defaultSmsRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
 | 
			
		||||
        AudioPlayerUriUtil.playAudio(service, defaultSmsRingtoneUri);
 | 
			
		||||
 | 
			
		||||
        // 播放应用铃声
 | 
			
		||||
        // 获取MP3文件的Uri
 | 
			
		||||
        Uri soundUri = Uri.parse("android.resource://" + service.getPackageName() + "/" + R.raw.diweiyi);
 | 
			
		||||
        AudioPlayerUriUtil.playAudio(service, soundUri);
 | 
			
		||||
		
 | 
			
		||||
		TimeStampUtil.getInstance(service).playNotifyMusic(service);
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@ package cc.winboll.studio.timestamp.utils;
 | 
			
		||||
 * @Describe TimeStampUtil
 | 
			
		||||
 */
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import cc.winboll.studio.timestamp.R;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.ZoneId;
 | 
			
		||||
@@ -55,4 +57,19 @@ public class TimeStampUtil {
 | 
			
		||||
        String formattedDateTime = ldt.format(formatter);
 | 
			
		||||
        return formattedDateTime;
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	//
 | 
			
		||||
	// 播放时间戳确定时的提示音乐
 | 
			
		||||
	//
 | 
			
		||||
	public static void playNotifyMusic(Context context) {
 | 
			
		||||
        // 播放默认短信铃声
 | 
			
		||||
        //Uri defaultSmsRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
 | 
			
		||||
        //AudioPlayerUriUtil.playAudio(context, defaultSmsRingtoneUri);
 | 
			
		||||
 | 
			
		||||
        // 播放应用铃声
 | 
			
		||||
        // 获取MP3文件的Uri
 | 
			
		||||
        Uri soundUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.diweiyi);
 | 
			
		||||
        AudioPlayerUriUtil.playAudio(context, soundUri);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,144 +1,180 @@
 | 
			
		||||
<?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:layout_width="match_parent"
 | 
			
		||||
	android:layout_height="match_parent"
 | 
			
		||||
	android:orientation="vertical">
 | 
			
		||||
	android:orientation="vertical"
 | 
			
		||||
	android:layout_width="wrap_content"
 | 
			
		||||
	android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
	<com.google.android.material.appbar.AppBarLayout
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
 | 
			
		||||
 | 
			
		||||
		<androidx.appcompat.widget.Toolbar
 | 
			
		||||
			android:id="@+id/toolbar"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="?attr/actionBarSize"
 | 
			
		||||
			app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
 | 
			
		||||
 | 
			
		||||
	</com.google.android.material.appbar.AppBarLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
		<Switch
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="启用时间戳常驻通知栏"
 | 
			
		||||
			android:id="@+id/activitymainSwitch1"
 | 
			
		||||
			android:onClick="onSetMainServiceStatus"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Show Format Preview:"
 | 
			
		||||
			android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Text"
 | 
			
		||||
			android:id="@+id/tv_timestampformatstring"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
		<EditText
 | 
			
		||||
			android:layout_width="0dp"
 | 
			
		||||
			android:ems="10"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/et_timestampformatstring"
 | 
			
		||||
			android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="ApplyShow"
 | 
			
		||||
			android:id="@+id/btn_saveformatstring"
 | 
			
		||||
			android:textAllCaps="false"
 | 
			
		||||
			android:onClick="onSaveFormatString"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Copy Format Preview:"
 | 
			
		||||
			android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
		<TextView
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="Text"
 | 
			
		||||
			android:id="@+id/tv_timestampcopyformatstring"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="horizontal"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="wrap_content"
 | 
			
		||||
		android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
		<EditText
 | 
			
		||||
			android:layout_width="0dp"
 | 
			
		||||
			android:ems="10"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:id="@+id/et_timestampcopyformatstring"
 | 
			
		||||
			android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
		<Button
 | 
			
		||||
			android:layout_width="wrap_content"
 | 
			
		||||
			android:layout_height="wrap_content"
 | 
			
		||||
			android:text="ApplyCopy"
 | 
			
		||||
			android:id="@+id/btn_savecopyformatstring"
 | 
			
		||||
			android:textAllCaps="false"
 | 
			
		||||
			android:onClick="onSaveCopyFormatString"/>
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0"
 | 
			
		||||
		android:gravity="center_vertical|center_horizontal">
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	<LinearLayout
 | 
			
		||||
		android:orientation="vertical"
 | 
			
		||||
	<ScrollView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="0dp"
 | 
			
		||||
		android:layout_weight="1.0">
 | 
			
		||||
 | 
			
		||||
		<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
		<LinearLayout
 | 
			
		||||
			xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
			android:layout_width="match_parent"
 | 
			
		||||
			android:layout_height="match_parent"
 | 
			
		||||
			android:id="@+id/logview"/>
 | 
			
		||||
			android:orientation="vertical">
 | 
			
		||||
 | 
			
		||||
	</LinearLayout>
 | 
			
		||||
			<com.google.android.material.appbar.AppBarLayout
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
 | 
			
		||||
 | 
			
		||||
				<androidx.appcompat.widget.Toolbar
 | 
			
		||||
					android:id="@+id/toolbar"
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="?attr/actionBarSize"
 | 
			
		||||
					app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
 | 
			
		||||
 | 
			
		||||
			</com.google.android.material.appbar.AppBarLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
				<Switch
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="启用时间戳常驻通知栏"
 | 
			
		||||
					android:id="@+id/activitymainSwitch1"
 | 
			
		||||
					android:onClick="onSetMainServiceStatus"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Show Format Preview:"
 | 
			
		||||
					android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Text"
 | 
			
		||||
					android:id="@+id/tv_timestampformatstring"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="0dp"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/et_timestampformatstring"
 | 
			
		||||
					android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="ApplyShow"
 | 
			
		||||
					android:id="@+id/btn_saveformatstring"
 | 
			
		||||
					android:textAllCaps="false"
 | 
			
		||||
					android:onClick="onSaveFormatString"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Copy Format Preview:"
 | 
			
		||||
					android:paddingRight="10dp"/>
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="Text"
 | 
			
		||||
					android:id="@+id/tv_timestampcopyformatstring"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:padding="10dp">
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="0dp"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/et_timestampcopyformatstring"
 | 
			
		||||
					android:layout_weight="1.0"/>
 | 
			
		||||
 | 
			
		||||
				<Button
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="ApplyCopy"
 | 
			
		||||
					android:id="@+id/btn_savecopyformatstring"
 | 
			
		||||
					android:textAllCaps="false"
 | 
			
		||||
					android:onClick="onSaveCopyFormatString"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<TextView
 | 
			
		||||
					android:layout_width="wrap_content"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:text="当前截取的时间戳为:"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="vertical"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content"
 | 
			
		||||
				android:gravity="center_vertical|center_horizontal">
 | 
			
		||||
 | 
			
		||||
				<ImageButton
 | 
			
		||||
					android:layout_width="100dp"
 | 
			
		||||
					android:layout_height="100dp"
 | 
			
		||||
					android:onClick="onTimeStamp"
 | 
			
		||||
					android:background="@drawable/ic_timestamp"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
			<LinearLayout
 | 
			
		||||
				android:orientation="horizontal"
 | 
			
		||||
				android:layout_width="match_parent"
 | 
			
		||||
				android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
				<EditText
 | 
			
		||||
					android:layout_width="match_parent"
 | 
			
		||||
					android:ems="10"
 | 
			
		||||
					android:layout_height="wrap_content"
 | 
			
		||||
					android:id="@+id/et_timestamp"/>
 | 
			
		||||
 | 
			
		||||
			</LinearLayout>
 | 
			
		||||
 | 
			
		||||
		</LinearLayout>
 | 
			
		||||
 | 
			
		||||
	</ScrollView>
 | 
			
		||||
 | 
			
		||||
	<cc.winboll.studio.libappbase.LogView
 | 
			
		||||
		android:layout_width="match_parent"
 | 
			
		||||
		android:layout_height="200dp"
 | 
			
		||||
		android:id="@+id/logview"/>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user