编译模块方式调整

This commit is contained in:
ZhanGSKen 2025-04-27 01:08:33 +08:00
commit 94375f34e2
440 changed files with 8972 additions and 41979 deletions

View File

@ -75,9 +75,9 @@ android {
def outputFileName=project.rootDir.name + "_${versionName}.apk" def outputFileName=project.rootDir.name + "_${versionName}.apk"
// WinBoll Studio // WinBoll Studio
File fWinBollStudioDir = file("/sdcard/WinBollStudio/APKs"); File fWinBoLLStudioDir = file("/sdcard/WinBoLLStudio/APKs");
if(!fWinBollStudioDir.exists()) { if(!fWinBoLLStudioDir.exists()) {
//fWinBollStudioDir.mkdirs(); //fWinBoLLStudioDir.mkdirs();
// APK发布和源码管理操作 // APK发布和源码管理操作
// WinBoll , APK发布和源码管理操作 // WinBoll , APK发布和源码管理操作
println 'The current compilation environment is not in WinBoll host, and the following APK publishing and source management operations will be ignore.' println 'The current compilation environment is not in WinBoll host, and the following APK publishing and source management operations will be ignore.'
@ -94,12 +94,12 @@ android {
// WinBoll // WinBoll
// //
if(variant.flavorName == "beta"&&variant.buildType.name == "debug"){ if(variant.flavorName == "beta"&&variant.buildType.name == "debug"){
//File outBuildBckDir = new File(fWinBollStudioDir, "/${rootProject.name}/${variant.buildType.name}") //File outBuildBckDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/${variant.buildType.name}")
File outBuildBckDir = new File(fWinBollStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}") File outBuildBckDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/${variant.buildType.name}")
// //
if(!outBuildBckDir.exists()) { if(!outBuildBckDir.exists()) {
outBuildBckDir.mkdirs(); outBuildBckDir.mkdirs();
println "Output Folder Created.(WinBollStudio) : " + outBuildBckDir.getAbsolutePath() println "Output Folder Created.(WinBoLLStudio) : " + outBuildBckDir.getAbsolutePath()
} }
if(outBuildBckDir.exists()) { if(outBuildBckDir.exists()) {
copy{ copy{
@ -108,7 +108,7 @@ android {
rename { rename {
String fileName -> "${outputFileName}" String fileName -> "${outputFileName}"
} }
println "Output APK (WinBollStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}" println "Output APK (WinBoLLStudio): " + outBuildBckDir.getAbsolutePath() + "/${outputFileName}"
} }
// //
assert (winbollBuildProps['buildCount'] != null) assert (winbollBuildProps['buildCount'] != null)
@ -151,8 +151,8 @@ android {
String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk" String szCommonTagAPKName = project.rootDir.name + "_" + szShortVersionName + ".apk"
println "CommonTagAPKName is : " + szCommonTagAPKName println "CommonTagAPKName is : " + szCommonTagAPKName
//File outTagDir = new File(fWinBollStudioDir, "/${rootProject.name}/tag/") //File outTagDir = new File(fWinBoLLStudioDir, "/${rootProject.name}/tag/")
File outTagDir = new File(fWinBollStudioDir, "/" + project.rootDir.name + "/tag/") File outTagDir = new File(fWinBoLLStudioDir, "/" + project.rootDir.name + "/tag/")
// //
if(!outTagDir.exists()) { if(!outTagDir.exists()) {
outTagDir.mkdirs(); outTagDir.mkdirs();

View File

@ -14,7 +14,7 @@ def DefaultGroupId = 'cc.winboll.studio' // 类库所有者groupId
def DefaultVersion = getDefaultVersion() // def DefaultVersion = getDefaultVersion() //
def DeveloperId='zhangsken' // def DeveloperId='zhangsken' //
def DeveloperName='ZhanGSKen' // def DeveloperName='ZhanGSKen' //
def DeveloperEMail='ZhanGSKen@QQ.COM' // def DeveloperEMail='zhangsken@188.com' //
def LicenseName='The Apache Software License, Version 2.0' def LicenseName='The Apache Software License, Version 2.0'
def LicenseUrl='http://www.apache.org/licenses/LICENSE-2.0.txt' def LicenseUrl='http://www.apache.org/licenses/LICENSE-2.0.txt'

View File

@ -9,8 +9,8 @@
## WinBoll 项目组研发计划 ## WinBoll 项目组研发计划
致力于把 WinBoll-APP 应用在手机端 Android 项目开发。 致力于把 WinBoll-APP 应用在手机端 Android 项目开发。
也在探索 https://gitea.winboll.cc/<WinBoll 项目组>/WinBoll-APP.git 应用于 WinBoll-APP APK 分发。 也在探索 https://gitea.winboll.cc/<WinBoll 项目组>/APP.git 应用于 WinBoll-APP APK 分发。
更想进阶 https://github.com/<WinBoll 项目组>/WinBoll-APP.git 应用于 WinBoll-APP Beta APK 分发。 更想进阶 https://github.com/<WinBoll 项目组>/APP.git 应用于 WinBoll-APP Beta APK 分发。
## WinBoll-APP 汗下... ## WinBoll-APP 汗下...
#### ☁应用何置如此呢。且观用户云云。 #### ☁应用何置如此呢。且观用户云云。
@ -55,10 +55,10 @@
☁ WinBoll 主机建立 1Panel Gitea 应用。 ☁ WinBoll 主机建立 1Panel Gitea 应用。
☁ WinBoll 主机设置 WinBoll 应用为非登录状态。 ☁ WinBoll 主机设置 WinBoll 应用为非登录状态。
☁ WinBoll 主机建立 WinBoll 账户与 WinBoll 用户组。 ☁ WinBoll 主机建立 WinBoll 账户与 WinBoll 用户组。
☁ WinBoll 账户 User ID 为: winboll ☁ WinBoll 账户 User ID 为: J
☁ WinBoll 用户组 Group ID 为: winboll ☁ WinBoll 用户组 Group ID 为: Studio
☁ WinBoll 主机 WinBoll 1Panel Gitea 建立 WinBoll 工作组。 ☁ WinBoll 主机 WinBoll 1Panel Gitea 建立 WinBoll 工作组。
☁ WinBoll 主机 WinBoll 1Panel Gitea 用户项目 APK 编译输出目录为 /sdcard/<用户名>/WinBoll/app/ ☁ WinBoll 主机 WinBoll 1Panel Gitea 用户项目 APK 编译输出目录为 /sdcard/WinBollStudio/<用户名>/APKs/
☁ WinBoll 项目配置文件示例为 "<WinBoll 项目根目录>/.winboll/winboll.properties-demo"(WinBoll 项目已设置) ☁ WinBoll 项目配置文件示例为 "<WinBoll 项目根目录>/.winboll/winboll.properties-demo"(WinBoll 项目已设置)
☁ WinBoll 项目配置文件为 "<WinBoll 项目根目录>/.winboll/winboll.properties" ☁ WinBoll 项目配置文件为 "<WinBoll 项目根目录>/.winboll/winboll.properties"
☁ WinBoll 项目配置文件设定为源码提交时忽略。(WinBoll 项目已设置) ☁ WinBoll 项目配置文件设定为源码提交时忽略。(WinBoll 项目已设置)
@ -72,17 +72,17 @@
☁ MySQL winbollclient 数据库中 ☁ MySQL winbollclient 数据库中
WinBoll 客户端用户信息设定为: WinBoll 客户端用户信息设定为:
<用户名, 验证密码, 验证邮箱, 验证手机, 唯一存储令牌Token, 备用验证邮箱> <用户名, 验证密码, 验证邮箱, 验证手机, 唯一存储令牌Token, 备用验证邮箱>
☁ WinBoll 项目源码仓库托管在 WinBoll 1Panel Gitea 目录 /opt/1panel/apps/gitea/gitea/data/git/repositories/winboll/winboll.git中。 ☁ WinBoll 项目源码仓库托管在 WinBoll 1Panel Gitea 目录 /opt/1panel/apps/gitea/gitea/data/git/repositories/studio/app.git中。
☁ WinBoll 主机提供 WinBoll 1Panel Gitea 应用的 WinBoll 项目源码仓库存取功能。Gitea 应用已提供) ☁ WinBoll 主机提供 WinBoll 1Panel Gitea 应用的 WinBoll 项目源码仓库存取功能。Gitea 应用已提供)
☁ WinBoll 主机提供 WinBoll Gitea 项目仓库存档功能。Gitea 应用已提供) ☁ WinBoll 主机提供 WinBoll Gitea 项目仓库存档功能。Gitea 应用已提供)
☁ 提供 WinBoll 客户端用户登录功能。Gitea 应用已提供) ☁ 提供 WinBoll 客户端用户登录功能。Gitea 应用已提供)
### ☁ 看远方 ☁ ### ### ☁ 看远方 ☁ ###
### ☁ 心忧虑 ☁ WinBoll-APP 应用前置需求 ### ☁ 心忧虑 ☁ WinBoll-APP 应用前置需求
☁ WinBoll-APP WinBoll 项目根目录设定为手机的 /sdcard/WinBoll 目录。(需要用户手动建立文件夹) ☁ WinBoll-APP WinBoll 项目根目录设定为手机的 /sdcard/WinBollStudio/Sources 目录。(需要用户手动建立文件夹)
☁ WinBoll-APP 具有手机 /sdcard/WinBoll 目录的存储权限。(需要手机操作系统授权) ☁ WinBoll-APP 具有手机 /sdcard/WinBoll 目录的存储权限。(需要手机操作系统授权)
☁ WinBoll-APP WinBoll 项目仓库源码存储路径为 /sdcard/WinBoll/repositories/winboll.git需要用户手动建立文件夹 ☁ WinBoll-APP WinBoll 项目仓库源码存储路径为 /sdcard/WinBollStudio/Sources/APP.git需要用户手动建立文件夹
☁ WinBoll-APP 项目 APK 编译输出目录为 /sdcard/WinBoll/app/ ☁ WinBoll-APP 项目 APK 编译输出目录为 /sdcard/WinBollStudio/APKs/
☁ WinBoll-APP 应用签名验证可定制化。WinBoll 项目已提供) ☁ WinBoll-APP 应用签名验证可定制化。WinBoll 项目已提供)
☁ WinBoll-APP 与系列衍生 APP 应用共享 cc.winboll.studio 命名空间资源。WinBoll 项目已提供) ☁ WinBoll-APP 与系列衍生 APP 应用共享 cc.winboll.studio 命名空间资源。WinBoll 项目已提供)
☁ WinBoll-APP 用户客户端信息存储在命名空间为 WinBoll APP MySQLLite 应用的 winbollappclient 数据库中。 ☁ WinBoll-APP 用户客户端信息存储在命名空间为 WinBoll APP MySQLLite 应用的 winbollappclient 数据库中。
@ -92,7 +92,12 @@
### ☁ 云游四方 ☁ ### ### ☁ 云游四方 ☁ ###
### ☁ 呔! ☁ WinBoll-APP 应用需求规划 ### ☁ 呔! ☁ WinBoll-APP 应用需求规划
☁ WinBoll-APP 提供手机目录 /sdcard/WinBoll 的 WinBoll 项目源码管理功能。 ☁ 如要使用 WinBoLL Android 项目的 Gradle 编译功能,则需要设置以下两个文件夹。
☁ 1. 则需要建立数据存储目录 /sdcard/WinBoLLStudio/APKs。
WinBoLL 项目源码编译出来的安装包会拷贝一份到 /sdcard/WinBoLLStudio/APKs 目录下。
☁ 2. 则需要建立数据存储目录 /sdcard/AppProjects。
WinBoLL 项目源码编译出来的安装包会拷贝一份并命名 "app.apk" 的安装文件为到 /sdcard/AppProjects 目录下。
### ☁ 吁! ☁ WinBoll-APP 共享计划前景 ### ☁ 吁! ☁ WinBoll-APP 共享计划前景
☁ WinBoll-APP 将会实现 https://winboll.cc/api 访问功能。 ☁ WinBoll-APP 将会实现 https://winboll.cc/api 访问功能。

View File

@ -19,17 +19,17 @@ def genVersionName(def versionName){
android { android {
compileSdkVersion 32 compileSdkVersion 32
buildToolsVersion "33.0.3" buildToolsVersion "32.0.0"
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.aes" applicationId "cc.winboll.studio.aes"
minSdkVersion 24 minSdkVersion 24
targetSdkVersion 30 targetSdkVersion 29
versionCode 1 versionCode 1
// versionName // versionName
// build.gradle stageCount=0 // build.gradle stageCount=0
// Gradle编译环境下合起来的 versionName "${versionName}.0" // Gradle编译环境下合起来的 versionName "${versionName}.0"
versionName "7.6" versionName "15.2"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }
@ -41,29 +41,9 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
} }
dependencies { dependencies {
api project(':libaes') api project(':libaes')
//api 'cc.winboll.studio:winboll-shared:1.6.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 'androidx.appcompat:appcompat:1.0.0'
api 'androidx.fragment:fragment:1.0.0'
api 'com.google.android.material:material:1.0.0'
api 'cc.winboll.studio:libapputils:9.2.1'
api 'cc.winboll.studio:libappbase:1.0.3'
api fileTree(dir: 'libs', include: ['*.jar']) api fileTree(dir: 'libs', include: ['*.jar'])
} }

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Sun Jan 19 04:58:59 GMT 2025 #Thu Apr 03 11:14:05 HKT 2025
stageCount=3 stageCount=7
libraryProject=libaes libraryProject=libaes
baseVersion=7.6 baseVersion=15.2
publishVersion=7.6.2 publishVersion=15.2.6
buildCount=4 buildCount=0
baseBetaVersion=7.6.3 baseBetaVersion=15.2.7

View File

@ -8,9 +8,10 @@
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/WinBoll.SupportThemeNoActionBar" android:theme="@style/MyAESTheme"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:supportsRtl="true"> android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
@ -30,6 +31,8 @@
android:name="android.max_aspect" android:name="android.max_aspect"
android:value="4.0"/> android:value="4.0"/>
<activity android:name=".AboutActivity"/>
</application> </application>
</manifest> </manifest>

View File

@ -0,0 +1,93 @@
package cc.winboll.studio.aes;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/24 23:52:29
* @Describe AES应用介绍窗口
*/
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libaes.winboll.AboutView;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
public class AboutActivity extends WinBollActivity implements IWinBollActivity {
public static final String TAG = "AboutActivity";
Context mContext;
Toolbar mToolbar;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_about);
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(TAG);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
AboutView aboutView = CreateAboutView();
// Activity onCreate 或其他生命周期方法中调用
// LinearLayout layout = new LinearLayout(this);
// layout.setOrientation(LinearLayout.VERTICAL);
// // 创建布局参数宽度和高度
// ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.MATCH_PARENT
// );
// addContentView(aboutView, params);
LinearLayout layout = findViewById(R.id.aboutviewroot_ll);
// 创建布局参数宽度和高度
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
layout.addView(aboutView, params);
GlobalApplication.getWinBollActivityManager().add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
GlobalApplication.getWinBollActivityManager().registeRemove(this);
}
public AboutView CreateAboutView() {
String szBranchName = "aes";
APPInfo appInfo = new APPInfo();
appInfo.setAppName("AES");
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
appInfo.setAppDescription("AES Description");
appInfo.setAppGitName("APP");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(szBranchName);
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=AES");
appInfo.setAppAPKName("AES");
appInfo.setAppAPKFolderName("AES");
//appInfo.setIsAddDebugTools(false);
appInfo.setIsAddDebugTools(BuildConfig.DEBUG);
return new AboutView(mContext, appInfo);
}
}

View File

@ -5,16 +5,26 @@ package cc.winboll.studio.aes;
* @Date 2024/06/13 19:03:58 * @Date 2024/06/13 19:03:58
* @Describe AES应用类 * @Describe AES应用类
*/ */
import android.view.Gravity;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import com.hjq.toast.ToastUtils;
public class App extends GlobalApplication { public class App extends GlobalApplication {
public static final String TAG = "App"; public static final String TAG = "App";
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
//setIsDebug(BuildConfig.DEBUG);
// 初始化 Toast 框架
ToastUtils.init(this);
// 设置 Toast 布局样式
ToastUtils.setView(R.layout.view_toast);
//ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
} }
} }

View File

@ -5,12 +5,193 @@ package cc.winboll.studio.aes;
* @Date 2024/06/13 19:05:52 * @Date 2024/06/13 19:05:52
* @Describe 应用主窗口 * @Describe 应用主窗口
*/ */
import cc.winboll.studio.libaes.unittests.LibraryActivity; import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Toast;
import cc.winboll.studio.aes.R;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
import cc.winboll.studio.libaes.beans.DrawerMenuBean;
import cc.winboll.studio.libaes.dialogs.LocalFileSelectDialog;
import cc.winboll.studio.libaes.dialogs.StoragePathDialog;
import cc.winboll.studio.libaes.unittests.SecondaryLibraryActivity;
import cc.winboll.studio.libaes.unittests.TestAButtonFragment;
import cc.winboll.studio.libaes.unittests.TestASupportToolbarActivity;
import cc.winboll.studio.libaes.unittests.TestAToolbarActivity;
import cc.winboll.studio.libaes.unittests.TestDrawerFragmentActivity;
import cc.winboll.studio.libaes.unittests.TestViewPageFragment;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import com.a4455jkjh.colorpicker.ColorPickerDialog;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
public class MainActivity extends DrawerFragmentActivity implements IWinBollActivity {
public class MainActivity extends LibraryActivity {
public static final String TAG = "MainActivity"; public static final String TAG = "MainActivity";
TestAButtonFragment mTestAButtonFragment;
TestViewPageFragment mTestViewPageFragment;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mTestAButtonFragment == null) {
mTestAButtonFragment = new TestAButtonFragment();
addFragment(mTestAButtonFragment);
}
showFragment(mTestAButtonFragment);
//setSubtitle(TAG);
//ToastUtils.show("onCreate");
}
@Override
public void initDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.initDrawerMenuItemList(listDrawerMenu);
LogUtils.d(TAG, "initDrawerMenuItemList");
//listDrawerMenu.clear();
// 添加抽屉菜单项
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG));
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG));
notifyDrawerMenuDataChanged();
}
@Override
public void reinitDrawerMenuItemList(ArrayList<DrawerMenuBean> listDrawerMenu) {
super.reinitDrawerMenuItemList(listDrawerMenu);
LogUtils.d(TAG, "reinitDrawerMenuItemList");
//listDrawerMenu.clear();
// 添加抽屉菜单项
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestAButtonFragment.TAG));
listDrawerMenu.add(new DrawerMenuBean(R.drawable.ic_launcher, TestViewPageFragment.TAG));
notifyDrawerMenuDataChanged();
}
@Override
public DrawerFragmentActivity.ActivityType initActivityType() {
return DrawerFragmentActivity.ActivityType.Main;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_library, menu);
if(App.isDebuging()) {
getMenuInflater().inflate(cc.winboll.studio.libapputils.R.menu.toolbar_studio_debug, menu);
}
return super.onCreateOptionsMenu(menu);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
super.onItemClick(parent, view, position, id);
switch (position) {
case 0 : {
if (mTestAButtonFragment == null) {
mTestAButtonFragment = new TestAButtonFragment();
addFragment(mTestAButtonFragment);
}
showFragment(mTestAButtonFragment);
break;
}
case 1 : {
if (mTestViewPageFragment == null) {
mTestViewPageFragment = new TestViewPageFragment();
addFragment(mTestViewPageFragment);
}
showFragment(mTestViewPageFragment);
break;
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int nItemId = item.getItemId();
// if (item.getItemId() == R.id.item_log) {
// WinBollActivityManager.getInstance(this).startWinBollActivity(getApplicationContext(), LogActivity.class);
// } else
if (nItemId == R.id.item_atoast) {
Toast.makeText(getApplication(), "item_testatoast", Toast.LENGTH_SHORT).show();
} else if (nItemId == R.id.item_atoolbar) {
Intent intent = new Intent(this, TestAToolbarActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_asupporttoolbar) {
Intent intent = new Intent(this, TestASupportToolbarActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_colordialog) {
ColorPickerDialog dlg = new ColorPickerDialog(this, getResources().getColor(R.color.colorPrimary));
dlg.setOnColorChangedListener(new com.a4455jkjh.colorpicker.view.OnColorChangedListener() {
@Override
public void beforeColorChanged() {
}
@Override
public void onColorChanged(int color) {
}
@Override
public void afterColorChanged() {
}
});
dlg.show();
} else if (nItemId == R.id.item_dialogstoragepath) {
final StoragePathDialog dialog = new StoragePathDialog(this, 0);
dialog.setOnOKClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.show();
} else if (nItemId == R.id.item_localfileselectdialog) {
final LocalFileSelectDialog dialog = new LocalFileSelectDialog(this);
dialog.setOnOKClickListener(new LocalFileSelectDialog.OKClickListener() {
@Override
public void onOKClick(String sz) {
Toast.makeText(getApplication(), sz, Toast.LENGTH_SHORT).show();
//dialog.dismiss();
}
});
dialog.open();
} else if (nItemId == R.id.item_secondarylibraryactivity) {
Intent intent = new Intent(this, SecondaryLibraryActivity.class);
startActivity(intent);
} else if (nItemId == R.id.item_drawerfragmentactivity) {
Intent intent = new Intent(this, TestDrawerFragmentActivity.class);
startActivity(intent);
}
else if (nItemId == R.id.item_about) {
Intent intent = new Intent(this, AboutActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
} }

View File

@ -0,0 +1,60 @@
package cc.winboll.studio.aes;
import android.app.Activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.beans.AESThemeBean;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import android.view.MenuItem;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/30 00:34:02
* @Describe WinBoll 活动窗口通用基类
*/
public class WinBollActivity extends AppCompatActivity implements IWinBollActivity {
public static final String TAG = "WinBollActivity";
protected volatile AESThemeBean.ThemeType mThemeType;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
mThemeType = getThemeType();
setThemeStyle();
super.onCreate(savedInstanceState);
}
AESThemeBean.ThemeType getThemeType() {
/*SharedPreferences sharedPreferences = getSharedPreferences(
SHAREDPREFERENCES_NAME, MODE_PRIVATE);
return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))];
*/
return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
}
void setThemeStyle() {
//setTheme(AESThemeBean.getThemeStyle(getThemeType()));
setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@ -1,29 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar <cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/activitymainToolbar1"/> android:id="@+id/toolbar"/>
<LinearLayout <LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:gravity="center" android:layout_weight="1.0"
android:layout_weight="1.0"> android:id="@+id/aboutviewroot_ll"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, WinBoll!"/>
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -13,7 +13,7 @@
<item <item
android:id="@+id/item_localfileselectdialog" android:id="@+id/item_localfileselectdialog"
android:title="LocalFileSelectDialog"/> android:title="LocalFileSelectDialog"/>
<item <item
android:id="@+id/item_atoolbar" android:id="@+id/item_atoolbar"
android:title="Test AToolbar"/> android:title="Test AToolbar"/>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#FF00B322</color>
<color name="colorPrimaryDark">#FF005C12</color>
<color name="colorAccent">#FF8DFFA2</color>
<color name="colorText">#FFFFFB8D</color>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <style name="MyAESTheme" parent="AESTheme">
</style> </style>
</resources> </resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">winboll.cc</domain>
</domain-config>
</network-security-config>

View File

@ -23,7 +23,7 @@ android {
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.androiddemo" applicationId "cc.winboll.studio.androiddemo"
minSdkVersion 26 minSdkVersion 24
targetSdkVersion 29 targetSdkVersion 29
versionCode 1 versionCode 1
// versionName // versionName
@ -49,7 +49,8 @@ dependencies {
// Android // Android
//api 'com.android.support:appcompat-v7:28.0.0' //api 'com.android.support:appcompat-v7:28.0.0'
api('com.android.support:appcompat-v7:28.0.0'){ api('com.android.support:appcompat-v7:28.0.0'){
exclude group: "com.android.support", module: "support-vector-drawable" //exclude group: "com.android.support", module: "support-vector-drawable"
exclude group: "com.android.support:animated-vector-drawable:28.0.0"
} }
// https://mvnrepository.com/artifact/com.android.support/support-compat // https://mvnrepository.com/artifact/com.android.support/support-compat
api 'com.android.support:support-compat:28.0.0' // api 'com.android.support:support-compat:28.0.0' //
@ -66,6 +67,6 @@ dependencies {
// https://mvnrepository.com/artifact/com.android.support/recyclerview-v7 // https://mvnrepository.com/artifact/com.android.support/recyclerview-v7
api 'com.android.support:recyclerview-v7:28.0.0' api 'com.android.support:recyclerview-v7:28.0.0'
api 'cc.winboll.studio:libappbase:15.0.9' api 'cc.winboll.studio:libapputils:15.2.2'
api 'cc.winboll.studio:libapputils:15.0.11' api 'cc.winboll.studio:libappbase:15.2.2'
} }

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Fri Mar 28 06:28:08 GMT 2025 #Thu Apr 03 03:17:18 GMT 2025
stageCount=0 stageCount=0
libraryProject= libraryProject=
baseVersion=15.0 baseVersion=15.0
publishVersion=15.0.0 publishVersion=15.0.0
buildCount=13 buildCount=21
baseBetaVersion=15.0.1 baseBetaVersion=15.0.1

View File

@ -23,7 +23,7 @@ android {
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.androidxdemo" applicationId "cc.winboll.studio.androidxdemo"
minSdkVersion 26 minSdkVersion 24
targetSdkVersion 29 targetSdkVersion 29
versionCode 1 versionCode 1
// versionName // versionName
@ -60,13 +60,14 @@ dependencies {
// //
api 'com.squareup.okhttp3:okhttp:4.4.1' api 'com.squareup.okhttp3:okhttp:4.4.1'
// AndroidX // AndroidX
api 'androidx.appcompat:appcompat:1.0.0' api 'androidx.appcompat:appcompat:1.1.0'
api 'com.google.android.material:material:1.4.0' api 'com.google.android.material:material:1.4.0'
//api 'androidx.viewpager:viewpager:1.0.0' //api 'androidx.viewpager:viewpager:1.0.0'
//api 'androidx.vectordrawable:vectordrawable:1.1.0' //api 'androidx.vectordrawable:vectordrawable:1.1.0'
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0' //api 'androidx.fragment:fragment:1.1.0'
api 'cc.winboll.studio:libappbase:15.0.9' api 'cc.winboll.studio:libaes:15.2.6'
api 'cc.winboll.studio:libapputils:15.0.11' api 'cc.winboll.studio:libapputils:15.2.2'
api 'cc.winboll.studio:libappbase:15.2.2'
} }

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Mon Mar 24 06:19:57 GMT 2025 #Thu Apr 03 03:15:55 GMT 2025
stageCount=0 stageCount=0
libraryProject= libraryProject=
baseVersion=15.0 baseVersion=15.0
publishVersion=15.0.0 publishVersion=15.0.0
buildCount=8 buildCount=18
baseBetaVersion=15.0.1 baseBetaVersion=15.0.1

View File

@ -14,6 +14,7 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -21,7 +22,9 @@ import android.widget.HorizontalScrollView;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import cc.winboll.studio.androidxdemo.R;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import com.hjq.toast.ToastUtils;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
@ -46,6 +49,14 @@ public class App extends GlobalApplication {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// 初始化 Toast 框架
ToastUtils.init(this);
// 设置 Toast 布局样式
ToastUtils.setView(R.layout.view_toast);
//ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
//CrashHandler.getInstance().registerGlobal(this); //CrashHandler.getInstance().registerGlobal(this);
//CrashHandler.getInstance().registerPart(this); //CrashHandler.getInstance().registerPart(this);
} }

View File

@ -4,6 +4,7 @@ import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogView; import cc.winboll.studio.libappbase.LogView;
import com.hjq.toast.ToastUtils;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@ -18,6 +19,8 @@ public class MainActivity extends AppCompatActivity {
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
mLogView = findViewById(R.id.logview); mLogView = findViewById(R.id.logview);
ToastUtils.show("onCreate");
} }
@Override @Override

View File

View File

View File

View File

View File

View File

View File

@ -1,8 +0,0 @@
#Created by .winboll/winboll_app_build.gradle
#Sun Jan 05 07:55:24 HKT 2025
stageCount=17
libraryProject=winboll-shared
baseVersion=1.8
publishVersion=1.8.16
buildCount=0
baseBetaVersion=1.8.17

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application
tools:replace="android:icon"
android:icon="@drawable/ic_winbollbeta">
<!-- Put flavor specific code here -->
</application>
</manifest>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WinBoll-APP+</string>
</resources>

View File

@ -1,48 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.app">
<!-- BIND_AUTOFILL_SERVICE -->
<uses-permission android:name="android.permission.BIND_AUTOFILL_SERVICE"/>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@drawable/ic_winboll"
android:label="@string/app_name"
android:theme="@style/WinBoll.SupportThemeNoActionBar"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/studio_provider"/>
</provider>
</application>
</manifest>

View File

@ -1,24 +0,0 @@
package cc.winboll.studio.app;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/12/08 15:10:51
* @Describe 全局应用类
*/
import cc.winboll.studio.shared.app.WinBollApplication;
import cc.winboll.studio.shared.log.LogUtils;
public class App extends WinBollApplication {
public static final String TAG = "App";
@Override
public void onCreate() {
// 必须在调用基类前设置应用调试标志
// 这样可以预先设置日志与数据的存储根目录
//setIsDebug(BuildConfig.DEBUG);
super.onCreate();
LogUtils.d(TAG, "onCreate");
}
}

View File

@ -1,168 +0,0 @@
package cc.winboll.studio.app;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.shared.app.WinBollActivity;
import cc.winboll.studio.shared.app.WinBollActivityManager;
import cc.winboll.studio.shared.app.WinBollApplication;
import cc.winboll.studio.shared.log.LogUtils;
import cc.winboll.studio.shared.util.UriUtils;
import cc.winboll.studio.shared.view.StringToQrCodeView;
import cc.winboll.studio.shared.view.YesNoAlertDialog;
import cc.winboll.studio.unittest.UnitTestActivity;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;
final public class MainActivity extends WinBollActivity {
public static final String TAG = "MainActivity";
public static final int REQUEST_HOME_ACTIVITY = 0;
public static final int REQUEST_ABOUT_ACTIVITY = 1;
@Override
protected boolean isEnableDisplayHomeAsUp() {
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// 接收并处理 Intent 数据函数 Intent 处理接收就直接返回
if (prosessIntents(getIntent())) return;
// 以下正常创建主窗口
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 设置 WinBoll 应用 UI 类型
WinBollApplication.setWinBollUI_TYPE(WinBollApplication.WinBollUI_TYPE.Aplication);
//ToastUtils.show("WinBollUI_TYPE " + WinBollApplication.getWinBollUI_TYPE());
LogUtils.d(TAG, "BuildConfig.DEBUG : " + Boolean.toString(BuildConfig.DEBUG));
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setSubTitle("");
}
//
// 处理传入的 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
protected boolean isAddWinBollToolBar() {
return true;
}
@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
protected Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_winboll_app_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.item_unittest) {
WinBollActivityManager.getInstance(this).startWinBollActivity(this, UnitTestActivity.class);
} else if (item.getItemId() == R.id.item_exit) {
exit();
return true;
}
return super.onOptionsItemSelected(item);
}
@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);
}
}
}
}

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:clickable="true">
<item android:drawable="@drawable/ic_launcher_background"/>
<item
android:left="15dp"
android:top="15dp"
android:right="15dp"
android:bottom="15dp"
android:drawable="@drawable/ic_launcher_foreground"/>
</layer-list>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activitymainToolbar1"/>
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center"
android:layout_weight="1.0">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, WinBoll!"/>
</LinearLayout>
</LinearLayout>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_gradient"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_launcher"/>
<TextView
android:id="@android:id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColor="#FF000000"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- WinBoll 默认方案 -->
<color name="colorPrimary">#FF196ABC</color>
<color name="colorPrimaryDark">#FF002B57</color>
<color name="colorAccent">#FF80BFFF</color>
<color name="colorToastFrame">#FFA9A9A9</color>
<color name="colorToastShadow">#FF000000</color>
<color name="colorToastBackgroung">#FFFFFFFF</color>
</resources>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WinBoll-APP</string>
</resources>

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_storage_root"
path="." />
<files-path
name="files_path"
path="." />
<cache-path
name="cache_path"
path="." />
<!--/storage/emulated/0/Android/data/...-->
<external-files-path
name="external_file_path"
path="." />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
<external-cache-path
name="external_cache_path"
path="." />
<!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录否则微信分身保存的图片就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg在小米6的手机上微信分身有这个crash华为没有
-->
<root-path
name="root_path"
path="" />
</paths>

View File

@ -30,7 +30,7 @@ android {
// versionName // versionName
// .winboll/winbollBuildProps.properties stageCount=0 // .winboll/winbollBuildProps.properties stageCount=0
// Gradle编译环境下合起来的 versionName "${versionName}.0" // Gradle编译环境下合起来的 versionName "${versionName}.0"
versionName "15.1" versionName "15.2"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Fri Mar 28 18:20:32 HKT 2025 #Sat Apr 26 16:44:11 GMT 2025
stageCount=4 stageCount=6
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.1 baseVersion=15.2
publishVersion=15.1.3 publishVersion=15.2.5
buildCount=0 buildCount=6
baseBetaVersion=15.1.4 baseBetaVersion=15.2.6

View File

@ -86,6 +86,7 @@ public class MainActivity extends WinBollActivityBase implements IWinBollActivit
public void onSwitchDebugMode(View view) { public void onSwitchDebugMode(View view) {
boolean isDebuging = ((CheckBox)view).isChecked(); boolean isDebuging = ((CheckBox)view).isChecked();
GlobalApplication.setIsDebuging(isDebuging); GlobalApplication.setIsDebuging(isDebuging);
GlobalApplication.saveDebugStatus();
} }
public void onPreviewGlobalCrashActivity(View view) { public void onPreviewGlobalCrashActivity(View view) {

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Thu Apr 03 11:09:41 HKT 2025 #Sat Apr 26 16:55:12 GMT 2025
stageCount=3 stageCount=2
libraryProject=libapputils libraryProject=libapputils
baseVersion=15.2 baseVersion=15.2
publishVersion=15.2.2 publishVersion=15.2.1
buildCount=0 buildCount=1
baseBetaVersion=15.2.3 baseBetaVersion=15.2.2

View File

@ -1,11 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
repositories { repositories {
// Nexus Maven // Maven ~/.m2/repository
// "WinBoll Release" //mavenLocal()
maven { url "https://nexus.winboll.cc/repository/maven-public/" } //
// "WinBoll Snapshot" //maven { url "file:///sdcard/.m2/repository" }
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } //
//maven { url "file:///sdcard/ZhanGSKen/AIDE/MavenReps/repository" }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }
maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/google/' }
@ -15,7 +16,13 @@ buildscript {
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
mavenCentral() mavenCentral()
google() google()
mavenLocal() //mavenLocal()
// Nexus Maven
// "WinBoll Release"
maven { url "https://192.168.123.26:8801/repository/maven-public/" }
// "WinBoll Snapshot"
maven { url "https://192.168.123.26:8801/repository/maven-snapshots/" }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.2.1' // compileSdkVersion 32 classpath 'com.android.tools.build:gradle:7.2.1' // compileSdkVersion 32
@ -26,11 +33,12 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
// Nexus Maven // Maven ~/.m2/repository
// "WinBoll Release" //mavenLocal()
maven { url "https://nexus.winboll.cc/repository/maven-public/" } //
// "WinBoll Snapshot" //maven { url "file:///sdcard/.m2/repository" }
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" } //
//maven { url "file:///sdcard/ZhanGSKen/AIDE/MavenReps/repository" }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }
maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/google/' }
@ -41,6 +49,12 @@ allprojects {
mavenCentral() mavenCentral()
google() google()
mavenLocal() mavenLocal()
// Nexus Maven
// "WinBoll Release"
maven { url "https://192.168.123.26:8801/repository/maven-public/" }
// "WinBoll Snapshot"
maven { url "https://192.168.123.26:8801/repository/maven-snapshots/" }
} }
ext { ext {
// //
@ -51,7 +65,8 @@ allprojects {
bashCommitAppPublishBuildFlagInfoFilePath = ".winboll/bashCommitAppPublishBuildFlagInfo.sh" bashCommitAppPublishBuildFlagInfoFilePath = ".winboll/bashCommitAppPublishBuildFlagInfo.sh"
winbollFilePath = "winboll.properties" winbollFilePath = "winboll.properties"
keyPropsFilePath = "winboll-x/current.keystore" //keyPropsFilePath = "winboll-x/current.keystore"
keyPropsFilePath = "current.keystore"
// lint // lint
lintXmlReportFilePath = "build/reports/lint-results.xml" lintXmlReportFilePath = "build/reports/lint-results.xml"
lintHTMLReportFilePath = "build/reports/lint-results.html" lintHTMLReportFilePath = "build/reports/lint-results.html"

View File

@ -19,17 +19,17 @@ def genVersionName(def versionName){
android { android {
compileSdkVersion 32 compileSdkVersion 32
buildToolsVersion "33.0.3" buildToolsVersion "32.0.0"
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.contacts" applicationId "cc.winboll.studio.contacts"
minSdkVersion 21 minSdkVersion 24
targetSdkVersion 30 targetSdkVersion 29
versionCode 1 versionCode 1
// versionName // versionName
// build.gradle stageCount=0 // build.gradle stageCount=0
// Gradle编译环境下合起来的 versionName "${versionName}.0" // Gradle编译环境下合起来的 versionName "${versionName}.0"
versionName "1.0" versionName "15.2"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }
@ -41,31 +41,48 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
} }
dependencies { dependencies {
// 使 api fileTree(dir: 'libs', include: ['*.jar'])
// https://github.com/getActivity/XXPermissions
api 'com.github.getActivity:XXPermissions:18.63'
//
api 'com.baoyz.pullrefreshlayout:library:1.2.0'
//
// https://mvnrepository.com/artifact/com.github.open-android/pinyin4j
api 'com.github.open-android:pinyin4j:2.5.0'
// SSH
api 'com.jcraft:jsch:0.1.55'
// Html
api 'org.jsoup:jsoup:1.13.1'
//
api 'com.google.zxing:core:3.4.1' api 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0' api 'com.journeyapps:zxing-android-embedded:3.6.0'
//
api 'io.github.medyo:android-about-page:2.0.0' api 'io.github.medyo:android-about-page:2.0.0'
//
api 'com.github.getActivity:ToastUtils:10.5' api 'com.github.getActivity:ToastUtils:10.5'
api 'com.jcraft:jsch:0.1.55' //
api 'org.jsoup:jsoup:1.13.1'
api 'com.squareup.okhttp3:okhttp:4.4.1' api 'com.squareup.okhttp3:okhttp:4.4.1'
// AndroidX
/*implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
implementation 'androidx.fragment:fragment:1.1.0'
implementation 'com.google.android.material:material:1.4.0'
*/
api 'androidx.appcompat:appcompat:1.1.0' api 'androidx.appcompat:appcompat:1.1.0'
api 'androidx.viewpager:viewpager:1.0.0'
api 'androidx.fragment:fragment:1.1.0'
api 'com.google.android.material:material:1.4.0' api 'com.google.android.material:material:1.4.0'
//api 'androidx.viewpager:viewpager:1.0.0'
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'
api 'cc.winboll.studio:libapputils:9.3.2' api 'cc.winboll.studio:libaes:15.2.4'
api 'cc.winboll.studio:libappbase:1.5.6' api 'cc.winboll.studio:libapputils:15.2.1'
api 'cc.winboll.studio:libappbase:15.2.2'
api fileTree(dir: 'libs', include: ['*.jar'])
} }

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Feb 25 00:17:29 HKT 2025 #Sun Apr 13 02:46:09 HKT 2025
stageCount=2 stageCount=8
libraryProject= libraryProject=
baseVersion=1.0 baseVersion=15.2
publishVersion=1.0.1 publishVersion=15.2.7
buildCount=0 buildCount=0
baseBetaVersion=1.0.2 baseBetaVersion=15.2.8

View File

@ -29,9 +29,13 @@
<!-- 更改您的音频设置 --> <!-- 更改您的音频设置 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS" /> <!-- 读取通话记录 -->
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<!-- 录音 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
@ -65,7 +69,7 @@
<activity <activity
android:name=".phonecallui.PhoneCallActivity" android:name=".phonecallui.PhoneCallActivity"
android:launchMode="singleInstance" android:launchMode="singleTask"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@ -182,6 +186,10 @@
</provider> </provider>
<activity android:name="cc.winboll.studio.contacts.activities.UnitTestActivity"/>
<activity android:name="cc.winboll.studio.contacts.activities.AboutActivity"/>
</application> </application>
</manifest> </manifest>

View File

@ -5,9 +5,10 @@ package cc.winboll.studio.contacts;
* @Date 2024/12/08 15:10:51 * @Date 2024/12/08 15:10:51
* @Describe 全局应用类 * @Describe 全局应用类
*/ */
import android.view.Gravity;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.winboll.WinBollActivityManager;
import cc.winboll.studio.libapputils.app.WinBollActivityManager; import com.hjq.toast.ToastUtils;
public class App extends GlobalApplication { public class App extends GlobalApplication {
@ -17,12 +18,20 @@ public class App extends GlobalApplication {
public void onCreate() { public void onCreate() {
// 必须在调用基类前设置应用调试标志 // 必须在调用基类前设置应用调试标志
// 这样可以预先设置日志与数据的存储根目录 // 这样可以预先设置日志与数据的存储根目录
setIsDebuging(this, BuildConfig.DEBUG); //setIsDebuging(BuildConfig.DEBUG);
super.onCreate(); super.onCreate();
// 设置 WinBoll 应用 UI 类型 // 设置 WinBoll 应用 UI 类型
WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication); WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
LogUtils.d(TAG, "onCreate"); //LogUtils.d(TAG, "onCreate");
// 初始化 Toast 框架
ToastUtils.init(this);
// 设置 Toast 布局样式
//ToastUtils.setView(R.layout.toast_custom_view);
//ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
} }
} }

View File

@ -1,52 +1,44 @@
package cc.winboll.studio.contacts; package cc.winboll.studio.contacts;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.role.RoleManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings;
import android.telecom.TelecomManager; import android.telecom.TelecomManager;
import android.view.LayoutInflater; import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat; import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.activities.CallActivity; import cc.winboll.studio.contacts.activities.SettingsActivity;
import cc.winboll.studio.contacts.adapters.MyPagerAdapter;
import cc.winboll.studio.contacts.beans.MainServiceBean; import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.fragments.CallLogFragment;
import cc.winboll.studio.contacts.fragments.ContactsFragment;
import cc.winboll.studio.contacts.fragments.LogFragment;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView; import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libapputils.app.IWinBollActivity; import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.APPInfo;
import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import android.content.DialogInterface;
import cc.winboll.studio.contacts.activities.SettingsActivity;
final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener { final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
@ -57,11 +49,13 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS"; public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS";
static MainActivity _MainActivity;
LogView mLogView; LogView mLogView;
Toolbar mToolbar; Toolbar mToolbar;
CheckBox cbMainService; CheckBox cbMainService;
MainServiceBean mMainServiceBean; MainServiceBean mMainServiceBean;
ViewPager viewPager; private TabLayout tabLayout;
private ViewPager viewPager;
private List<View> views; //用来存放放进ViewPager里面的布局 private List<View> views; //用来存放放进ViewPager里面的布局
//实例化存储imageView导航原点的集合 //实例化存储imageView导航原点的集合
ImageView[] imageViews; ImageView[] imageViews;
@ -70,15 +64,20 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
LinearLayout linearLayout;//下标所在在LinearLayout布局里 LinearLayout linearLayout;//下标所在在LinearLayout布局里
int currentPoint = 0;//当前被选中中页面的下标 int currentPoint = 0;//当前被选中中页面的下标
private TelephonyManager telephonyManager;
private MyPhoneStateListener phoneStateListener;
List<Fragment> fragmentList;
List<String> tabTitleList;
private static final int DIALER_REQUEST_CODE = 1; private static final int DIALER_REQUEST_CODE = 1;
@Override @Override
public AppCompatActivity getActivity() { public Activity getActivity() {
return this; return this;
} }
@Override // @Override
public APPInfo getAppInfo() { // public APPInfo getAppInfo() {
// String szBranchName = "contacts"; // String szBranchName = "contacts";
// //
// APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo(); // APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
@ -93,8 +92,8 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
// appInfo.setAppAPKName("Contacts"); // appInfo.setAppAPKName("Contacts");
// appInfo.setAppAPKFolderName("Contacts"); // appInfo.setAppAPKFolderName("Contacts");
// return appInfo; // return appInfo;
return null; // return null;
} // }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -102,28 +101,51 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
//if (prosessIntents(getIntent())) return; //if (prosessIntents(getIntent())) return;
// 以下正常创建主窗口 // 以下正常创建主窗口
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
_MainActivity = this;
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
// 初始化工具栏 // 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1); mToolbar = findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar); setSupportActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) { // if (isEnableDisplayHomeAsUp()) {
// 显示后退按钮 // // 显示后退按钮
getSupportActionBar().setDisplayHomeAsUpEnabled(true); // getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} // }
getSupportActionBar().setSubtitle(getTag()); getSupportActionBar().setSubtitle(getTag());
initData(); tabLayout = findViewById(R.id.tabLayout);
initView(); viewPager = findViewById(R.id.viewPager);
//initPoint();//调用初始化导航原点的方法
viewPager.addOnPageChangeListener(this);//滑动事件
ViewPager viewPager = findViewById(R.id.activitymainViewPager1); // 创建Fragment列表和标题列表
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); fragmentList = new ArrayList<>();
viewPager.setAdapter(pagerAdapter); tabTitleList = new ArrayList<>();
TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1); fragmentList.add(CallLogFragment.newInstance(0));
fragmentList.add(ContactsFragment.newInstance(1));
fragmentList.add(LogFragment.newInstance(2));
tabTitleList.add("通话记录");
tabTitleList.add("联系人");
tabTitleList.add("应用日志");
// 设置ViewPager的适配器
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
viewPager.setAdapter(adapter);
// 关联TabLayout和ViewPager
tabLayout.setupWithViewPager(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); // mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
// if (mMainServiceBean == null) { // if (mMainServiceBean == null) {
// mMainServiceBean = new MainServiceBean(); // mMainServiceBean = new MainServiceBean();
@ -140,36 +162,86 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
// } // }
// } // }
// }); // });
MainService.startMainService(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);
}
// 初始化TelephonyManager和PhoneStateListener
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
phoneStateListener = new MyPhoneStateListener();
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
// ViewPager的适配器
private class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;
private List<String> tabTitleList;
public MyPagerAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> tabTitleList) {
super(fm);
this.fragmentList = fragmentList;
this.tabTitleList = tabTitleList;
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return tabTitleList.get(position);
}
}
public static void dialPhoneNumber(String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
if (ActivityCompat.checkSelfPermission(_MainActivity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
_MainActivity.startActivity(intent);
} }
//初始化view即显示的图片 //初始化view即显示的图片
void initView() { // void initView() {
viewPager = findViewById(R.id.activitymainViewPager1); // viewPager = findViewById(R.id.activitymainViewPager1);
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); // pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter); // viewPager.setAdapter(pagerAdapter);
//adapter = new MyPagerAdapter(views); // //adapter = new MyPagerAdapter(views);
//viewPager = findViewById(R.id.activitymainViewPager1); // //viewPager = findViewById(R.id.activitymainViewPager1);
//viewPager.setAdapter(adapter); // //viewPager.setAdapter(adapter);
//linearLayout = findViewById(R.id.activitymainLinearLayout1); // //linearLayout = findViewById(R.id.activitymainLinearLayout1);
//initPoint();//初始化页面下方的点 // //initPoint();//初始化页面下方的点
viewPager.setOnPageChangeListener(this); // viewPager.setOnPageChangeListener(this);
//
} // }
//初始化所要显示的布局 //初始化所要显示的布局
void initData() { // void initData() {
ViewPager viewPager = findViewById(R.id.activitymainViewPager1); // LayoutInflater inflater = LayoutInflater.from(getActivity());
LayoutInflater inflater = LayoutInflater.from(getActivity()); // View view1 = inflater.inflate(R.layout.fragment_call_log, viewPager, false);
View view1 = inflater.inflate(R.layout.fragment_call, viewPager, false); // View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false); // View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false); //
// views = new ArrayList<>();
views = new ArrayList<>(); // views.add(view1);
views.add(view1); // views.add(view2);
views.add(view2); // views.add(view3);
views.add(view3); // }
}
// void initPoint() { // void initPoint() {
// imageViews = new ImageView[5];//实例化5个图片 // imageViews = new ImageView[5];//实例化5个图片
@ -231,6 +303,23 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
//setSubTitle(""); //setSubTitle("");
} }
private class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
LogUtils.d(TAG, "电话已挂断");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
LogUtils.d(TAG, "正在通话中");
break;
case TelephonyManager.CALL_STATE_RINGING:
LogUtils.d(TAG, "来电: " + incomingNumber);
break;
}
}
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
@ -287,40 +376,25 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
return TAG; return TAG;
} }
@Override // @Override
public Toolbar initToolBar() { // public void onBackPressed() {
return findViewById(R.id.activitymainToolbar1); // exit();
} // }
//
@Override // void exit() {
public boolean isAddWinBollToolBar() { // YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
return true; //
} // @Override
// public void onYes() {
@Override // WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
public boolean isEnableDisplayHomeAsUp() { // }
return false; //
} // @Override
// public void onNo() {
@Override // }
public void onBackPressed() { // };
exit(); // YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
} // }
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 @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
@ -331,11 +405,7 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.item_call) { if (item.getItemId() == R.id.item_settings) {
Intent intent = new Intent(this, CallActivity.class);
startActivity(intent);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
} else if (item.getItemId() == R.id.item_settings) {
Intent intent = new Intent(this, SettingsActivity.class); Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent); startActivity(intent);
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class); //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);

View File

@ -1,35 +0,0 @@
package cc.winboll.studio.contacts;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 21:14:52
* @Describe PhoneCallManager
*/
import android.telecom.Call;
import android.telecom.VideoProfile;
public class PhoneCallManager {
public static final String TAG = "PhoneCallManager";
public static Call call;
/**
* 接听电话
*/
public void answer() {
if (call != null) {
call.answer(VideoProfile.STATE_AUDIO_ONLY);
}
}
/**
* 断开电话包括来电时的拒接以及接听后的挂断
*/
public void disconnect() {
if (call != null) {
call.disconnect();
}
}
}

View File

@ -0,0 +1,91 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/31 15:15:54
* @Describe 应用介绍窗口
*/
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libaes.winboll.AboutView;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
public class AboutActivity extends WinBollActivity implements IWinBollActivity {
public static final String TAG = "AboutActivity";
Context mContext;
Toolbar mToolbar;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_about);
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(TAG);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
AboutView aboutView = CreateAboutView();
// Activity onCreate 或其他生命周期方法中调用
// LinearLayout layout = new LinearLayout(this);
// layout.setOrientation(LinearLayout.VERTICAL);
// // 创建布局参数宽度和高度
// ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.MATCH_PARENT
// );
// addContentView(aboutView, params);
LinearLayout layout = findViewById(R.id.aboutviewroot_ll);
// 创建布局参数宽度和高度
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
layout.addView(aboutView, params);
GlobalApplication.getWinBollActivityManager().add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
GlobalApplication.getWinBollActivityManager().registeRemove(this);
}
public AboutView CreateAboutView() {
String szBranchName = "contacts";
APPInfo appInfo = new APPInfo();
appInfo.setAppName("Contacts");
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
appInfo.setAppDescription("通讯录与拨号");
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 new AboutView(mContext, appInfo);
}
}

View File

@ -34,6 +34,7 @@ public class CallActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main); //setContentView(R.layout.activity_main);
setContentView(R.layout.activity_call); setContentView(R.layout.activity_call);

View File

@ -4,7 +4,6 @@ package cc.winboll.studio.contacts.activities;
* @Author ZhanGSKen@AliYun.Com * @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/21 05:37:42 * @Date 2025/02/21 05:37:42
*/ */
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@ -15,17 +14,33 @@ import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.adapters.PhoneConnectRuleAdapter;
import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
import cc.winboll.studio.contacts.beans.RingTongBean; import cc.winboll.studio.contacts.beans.RingTongBean;
import cc.winboll.studio.libappbase.IWinBollActivity; import cc.winboll.studio.contacts.beans.SettingsModel;
import cc.winboll.studio.libappbase.bean.APPInfo; import cc.winboll.studio.contacts.bobulltoon.TomCat;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.views.DuInfoTextView;
import cc.winboll.studio.libaes.winboll.APPInfo;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List;
import cc.winboll.studio.contacts.App;
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity { public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
@ -33,11 +48,25 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
Toolbar mToolbar; Toolbar mToolbar;
Switch swSilent; Switch swSilent;
SeekBar msbVolume;
TextView mtvVolume;
int mnStreamMaxVolume;
int mnStreamVolume;
Switch mswMainService;
static DuInfoTextView _DuInfoTextView;
@Override // 云盾防御层数量
public APPInfo getAppInfo() { EditText etDunTotalCount;
return null; // 防御层恢复时间间隔(秒钟)
} EditText etDunResumeSecondCount;
// 每次恢复防御层数
EditText etDunResumeCount;
// 是否启用云盾
Switch swIsEnableDun;
private RecyclerView recyclerView;
private PhoneConnectRuleAdapter adapter;
private List<PhoneConnectRuleModel> ruleList;
@Override @Override
public AppCompatActivity getActivity() { public AppCompatActivity getActivity() {
@ -49,21 +78,6 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
return TAG; return TAG;
} }
@Override
public Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean isAddWinBollToolBar() {
return true;
}
@Override
public boolean isEnableDisplayHomeAsUp() {
return false;
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -72,11 +86,136 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
// 初始化工具栏 // 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1); mToolbar = findViewById(R.id.activitymainToolbar1);
setSupportActionBar(mToolbar); setSupportActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) { // 显示后退按钮
// 显示后退按钮 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
getSupportActionBar().setSubtitle(getTag()); getSupportActionBar().setSubtitle(getTag());
mswMainService = findViewById(R.id.sw_mainservice);
MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
mswMainService.setChecked(mMainServiceBean.isEnable());
mswMainService.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View arg0) {
LogUtils.d(TAG, "mswMainService onClick");
// TODO: Implement this method
if (mswMainService.isChecked()) {
//ToastUtils.show("Is Checked");
MainService.startMainServiceAndSaveStatus(SettingsActivity.this);
} else {
//ToastUtils.show("Not Checked");
MainService.stopMainServiceAndSaveStatus(SettingsActivity.this);
}
}
});
msbVolume = findViewById(R.id.bellvolume);
mtvVolume = findViewById(R.id.tv_volume);
final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// 设置SeekBar的最大值为系统铃声音量的最大刻度
mnStreamMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
msbVolume.setMax(mnStreamMaxVolume);
// 获取当前铃声音量并设置为SeekBar的初始进度
mnStreamVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
msbVolume.setProgress(mnStreamVolume);
updateStreamVolumeTextView();
msbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
// 设置铃声音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, progress, 0);
RingTongBean bean = RingTongBean.loadBean(SettingsActivity.this, RingTongBean.class);
if (bean == null) {
bean = new RingTongBean();
}
bean.setStreamVolume(progress);
RingTongBean.saveBean(SettingsActivity.this, bean);
mnStreamVolume = progress;
updateStreamVolumeTextView();
//Toast.makeText(SettingsActivity.this, "音量设置为: " + progress, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 当开始拖动SeekBar时的操作
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 当停止拖动SeekBar时的操作
}
});
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
ruleList = Rules.getInstance(this).getPhoneBlacRuleBeanList();
adapter = new PhoneConnectRuleAdapter(this, ruleList);
recyclerView.setAdapter(adapter);
// 设置参数云盾
_DuInfoTextView = findViewById(R.id.tv_DunInfo);
etDunTotalCount = findViewById(R.id.et_DunTotalCount);
etDunResumeSecondCount = findViewById(R.id.et_DunResumeSecondCount);
etDunResumeCount = findViewById(R.id.et_DunResumeCount);
swIsEnableDun = findViewById(R.id.sw_IsEnableDun);
SettingsModel settingsModel = Rules.getInstance(this).getSettingsModel();
etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
swIsEnableDun.setChecked(settingsModel.isEnableDun());
boolean isEnableDun = settingsModel.isEnableDun();
etDunTotalCount.setEnabled(!isEnableDun);
etDunResumeSecondCount.setEnabled(!isEnableDun);
etDunResumeCount.setEnabled(!isEnableDun);
}
public static void notifyDunInfoUpdate() {
if (_DuInfoTextView != null) {
_DuInfoTextView.notifyInfoUpdate();
}
}
public void onSW_IsEnableDun(View view) {
LogUtils.d(TAG, "onSW_IsEnableDun");
boolean isEnableDun = swIsEnableDun.isChecked();
etDunTotalCount.setEnabled(!isEnableDun);
etDunResumeSecondCount.setEnabled(!isEnableDun);
etDunResumeCount.setEnabled(!isEnableDun);
SettingsModel settingsModel = Rules.getInstance(this).getSettingsModel();
if (isEnableDun) {
settingsModel.setDunTotalCount(Integer.parseInt(etDunTotalCount.getText().toString()));
settingsModel.setDunResumeSecondCount(Integer.parseInt(etDunResumeSecondCount.getText().toString()));
settingsModel.setDunResumeCount(Integer.parseInt(etDunResumeCount.getText().toString()));
}
settingsModel.setIsEnableDun(isEnableDun);
Rules.getInstance(this).saveDun();
Rules.getInstance(this).reload();
}
void updateStreamVolumeTextView() {
mtvVolume.setText(String.format("%d/%d", mnStreamVolume, mnStreamMaxVolume));
}
public void onUnitTest(View view) {
Intent intent = new Intent(this, UnitTestActivity.class);
startActivity(intent);
}
public void onAddNewConnectionRule(View view) {
Rules.getInstance(this).getPhoneBlacRuleBeanList().add(new PhoneConnectRuleModel());
Rules.getInstance(this).saveRules();
adapter.notifyDataSetChanged();
} }
public void onDefaultPhone(View view) { public void onDefaultPhone(View view) {
@ -94,6 +233,38 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
} }
} }
public void onDownloadBoBullToon(View view) {
final TomCat tomCat = TomCat.getInstance(this);
new Thread(new Runnable() {
@Override
public void run() {
if (tomCat.downloadBoBullToon()) {
ToastUtils.show("BoBullToon downlaod OK!");
MainService.restartMainService(SettingsActivity.this);
Rules.getInstance(SettingsActivity.this).reload();
}
}
}).start();
}
public void onSearchBoBullToonPhone(View view) {
TomCat tomCat = TomCat.getInstance(this);
EditText etPhone = findViewById(R.id.activitysettingsEditText1);
String phone = etPhone.getText().toString().trim();
if (tomCat.loadPhoneBoBullToon()) {
if (tomCat.isPhoneBoBullToon(phone)) {
ToastUtils.show("It is a BoBullToon Phone!");
} else {
ToastUtils.show("Not in BoBullToon.");
}
} else {
ToastUtils.show("没有下载 BoBullToon。");
}
}
private void askForDrawOverlay() { private void askForDrawOverlay() {
AlertDialog alertDialog = new AlertDialog.Builder(this) AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("允许显示悬浮框") .setTitle("允许显示悬浮框")
@ -140,4 +311,8 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
} }
} }
} }
public void onAbout(View view) {
App.getWinBollActivityManager().startWinBollActivity(this, AboutActivity.class);
}
} }

View File

@ -0,0 +1,94 @@
package cc.winboll.studio.contacts.activities;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.utils.IntUtils;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 16:07:04
*/
public class UnitTestActivity extends Activity {
public static final String TAG = "UnitTestActivity";
LogView logView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_unittest);
logView = findViewById(R.id.logview);
logView.start();
}
public void onTestPhone(View view) {
// 开始测试数据
EditText etPhone = findViewById(R.id.phone_et);
Rules rules = Rules.getInstance(this);
String phone = etPhone.getText().toString().trim();
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
}
public void onTestMain(View view) {
LogUtils.d(TAG, "IntUtils.unittest_getIntInRange();");
IntUtils.unittest_getIntInRange();
Rules rules = Rules.getInstance(this);
// 如果没有规则就添加测试规则
if (rules.getPhoneBlacRuleBeanList().size() == 0) {
// 手机号码允许
// 中国手机号码正则表达式以1开头第二位可以是3456789后面跟9位数字
String regex = "^1[3-9]\\d{9}$";
rules.add(regex, true, true);
// 指定区号号码允许
regex = "^0660\\d+$";
rules.add(regex, true, true);
// 指定区号号码允许
regex = "^020\\d+$";
rules.add(regex, true, true);
// 添加默认拒接规则
regex = ".*";
rules.add(regex, false, true);
// 保存规则到文件
rules.saveRules();
}
// 开始测试数据
String phone = "16769764848";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "16856582777";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "17519703124";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "0205658955";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "0108965253";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "+8616769764848";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "4005816769764848";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
phone = "95566";
LogUtils.d(TAG, String.format("Test phone : %s\n%s", phone, rules.isAllowed(phone)));
}
}

View File

@ -0,0 +1,60 @@
package cc.winboll.studio.contacts.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/31 15:16:45
* @Describe 应用窗口基类
*/
import android.app.Activity;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.beans.AESThemeBean;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libappbase.winboll.IWinBollActivity;
public class WinBollActivity extends AppCompatActivity implements IWinBollActivity {
public static final String TAG = "WinBollActivity";
protected volatile AESThemeBean.ThemeType mThemeType;
@Override
public Activity getActivity() {
return this;
}
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
mThemeType = getThemeType();
setThemeStyle();
super.onCreate(savedInstanceState);
}
AESThemeBean.ThemeType getThemeType() {
/*SharedPreferences sharedPreferences = getSharedPreferences(
SHAREDPREFERENCES_NAME, MODE_PRIVATE);
return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))];
*/
return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
}
void setThemeStyle() {
//setTheme(AESThemeBean.getThemeStyle(getThemeType()));
setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@ -0,0 +1,93 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:09:32
* @Describe CallLogAdapter
*/
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.CallLogModel;
import cc.winboll.studio.contacts.utils.ContactUtils;
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
import com.hjq.toast.ToastUtils;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogViewHolder> {
public static final String TAG = "CallLogAdapter";
private List<CallLogModel> callLogList;
ContactUtils mContactUtils;
Context mContext;
public CallLogAdapter(Context context, List<CallLogModel> callLogList) {
mContext = context;
this.mContactUtils = ContactUtils.getInstance(mContext);
this.callLogList = callLogList;
}
@NonNull
@Override
public CallLogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_call_log, parent, false);
return new CallLogViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull CallLogViewHolder holder, int position) {
final CallLogModel callLog = callLogList.get(position);
holder.phoneNumber.setText(callLog.getPhoneNumber() + "" + mContactUtils.getContactsName(callLog.getPhoneNumber()));
holder.callStatus.setText(callLog.getCallStatus());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
holder.callDate.setText(dateFormat.format(callLog.getCallDate()));
// 初始化拉动后拨号控件
holder.dialAOHPCTCSeekBar.setThumb(holder.itemView.getContext().getDrawable(R.drawable.ic_call));
holder.dialAOHPCTCSeekBar.setBlurRightDP(80);
holder.dialAOHPCTCSeekBar.setThumbOffset(0);
holder.dialAOHPCTCSeekBar.setOnOHPCListener(
new AOHPCTCSeekBar.OnOHPCListener(){
@Override
public void onOHPCommit() {
String phoneNumber = callLog.getPhoneNumber().replaceAll("\\s", "");
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);
holder.itemView.getContext().startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return callLogList.size();
}
public class CallLogViewHolder extends RecyclerView.ViewHolder {
TextView phoneNumber, callStatus, callDate;
Button dialButton;
AOHPCTCSeekBar dialAOHPCTCSeekBar;
public CallLogViewHolder(@NonNull View itemView) {
super(itemView);
phoneNumber = itemView.findViewById(R.id.phone_number);
callStatus = itemView.findViewById(R.id.call_status);
callDate = itemView.findViewById(R.id.call_date);
dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);
}
}
}

View File

@ -0,0 +1,84 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:35:44
* @Describe ContactAdapter
*/
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.ContactModel;
import com.hjq.toast.ToastUtils;
import java.util.List;
import cc.winboll.studio.libaes.views.AOHPCTCSeekBar;
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {
public static final String TAG = "ContactAdapter";
private static final int REQUEST_CALL_PHONE = 1;
private List<ContactModel> contactList;
public ContactAdapter(List<ContactModel> contactList) {
this.contactList = contactList;
}
@NonNull
@Override
public ContactViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contact, parent, false);
return new ContactViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ContactViewHolder holder, int position) {
final ContactModel contact = contactList.get(position);
holder.contactName.setText(contact.getName());
holder.contactNumber.setText(contact.getNumber());
// 初始化拉动后拨号控件
holder.dialAOHPCTCSeekBar.setThumb(holder.itemView.getContext().getDrawable(R.drawable.ic_call));
holder.dialAOHPCTCSeekBar.setBlurRightDP(80);
holder.dialAOHPCTCSeekBar.setThumbOffset(0);
holder.dialAOHPCTCSeekBar.setOnOHPCListener(
new AOHPCTCSeekBar.OnOHPCListener(){
@Override
public void onOHPCommit() {
String phoneNumber = contact.getNumber().replaceAll("\\s", "");
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);
holder.itemView.getContext().startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return contactList.size();
}
public class ContactViewHolder extends RecyclerView.ViewHolder {
TextView contactName;
TextView contactNumber;
AOHPCTCSeekBar dialAOHPCTCSeekBar;
public ContactViewHolder(@NonNull View itemView) {
super(itemView);
contactName = itemView.findViewById(R.id.contact_name);
contactNumber = itemView.findViewById(R.id.contact_number);
dialAOHPCTCSeekBar = itemView.findViewById(R.id.aohpctcseekbar_dial);
}
}
}

View File

@ -1,42 +0,0 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 13:33:04
* @Describe MyPagerAdapter
*/
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import cc.winboll.studio.contacts.fragments.CallFragment;
import cc.winboll.studio.contacts.fragments.ContactsFragment;
import cc.winboll.studio.contacts.fragments.LogFragment;
public class MyPagerAdapter extends FragmentPagerAdapter {
public static final String TAG = "MyPagerAdapter";
private static final int PAGE_COUNT = 3;
public MyPagerAdapter(@NonNull FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NonNull
@Override
public Fragment getItem(int position) {
if(position == 1) {
return ContactsFragment.newInstance(position);
} else if(position == 2) {
return LogFragment.newInstance(position);
} else {
return CallFragment.newInstance(position);
}
}
@Override
public int getCount() {
return PAGE_COUNT;
}
}

View File

@ -0,0 +1,247 @@
package cc.winboll.studio.contacts.adapters;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 17:27:41
* @Describe PhoneConnectRuleAdapter
*/
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.views.LeftScrollView;
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
import java.util.List;
public class PhoneConnectRuleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final String TAG = "PhoneConnectRuleAdapter";
private static final int VIEW_TYPE_SIMPLE = 0;
private static final int VIEW_TYPE_EDIT = 1;
private Context context;
private List<PhoneConnectRuleModel> ruleList;
public PhoneConnectRuleAdapter(Context context, List<PhoneConnectRuleModel> ruleList) {
this.context = context;
this.ruleList = ruleList;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
if (viewType == VIEW_TYPE_SIMPLE) {
View view = inflater.inflate(R.layout.view_phone_connect_rule_simple, parent, false);
return new SimpleViewHolder(parent, view);
} else {
View view = inflater.inflate(R.layout.view_phone_connect_rule, parent, false);
return new EditViewHolder(parent, view);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
final PhoneConnectRuleModel model = ruleList.get(position);
if (holder instanceof SimpleViewHolder) {
final SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder;
String szView = model.getRuleText().trim().equals("") ?"[NULL]": model.getRuleText();
simpleViewHolder.tvRuleText.setText(szView);
simpleViewHolder.scrollView.setOnActionListener(new LeftScrollView.OnActionListener(){
@Override
public void onUp() {
ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
if (position > 0) {
ToastUtils.show("onUp");
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
// PhoneConnectRuleModel newBean = new PhoneConnectRuleModel();
// newBean.setRuleText(list.get(position).getRuleText());
// newBean.setIsAllowConnection(list.get(position).isAllowConnection());
// newBean.setIsEnable(list.get(position).isEnable());
// newBean.setIsSimpleView(list.get(position).isSimpleView());
list.add(position - 1, list.get(position));
list.remove(position + 1);
Rules.getInstance(context).saveRules();
notifyDataSetChanged();
}
}
@Override
public void onDown() {
ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
if (position < list.size() - 1) {
ToastUtils.show("onDown");
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
// PhoneConnectRuleModel newBean = new PhoneConnectRuleModel();
// newBean.setRuleText(list.get(position).getRuleText());
// newBean.setIsAllowConnection(list.get(position).isAllowConnection());
// newBean.setIsEnable(list.get(position).isEnable());
// newBean.setIsSimpleView(list.get(position).isSimpleView());
list.add(position + 2, list.get(position));
list.remove(position);
Rules.getInstance(context).saveRules();
notifyDataSetChanged();
}
}
@Override
public void onEdit() {
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
model.setIsSimpleView(false);
notifyDataSetChanged();
//notifyItemChanged(position);
}
@Override
public void onDelete() {
YesNoAlertDialog.show(simpleViewHolder.scrollView.getContext(), "删除确认", "是否删除该通话规则?", new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onYes() {
simpleViewHolder.scrollView.smoothScrollTo(0, 0);
model.setIsSimpleView(true);
ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
list.remove(position);
Rules.getInstance(context).saveRules();
notifyDataSetChanged();
//notifyItemChanged(position);
}
@Override
public void onNo() {
}
});
}
});
// simpleViewHolder.editButton.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// model.setIsSimpleView(false);
// notifyItemChanged(position);
// }
// });
// simpleViewHolder.deleteButton.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// model.setIsSimpleView(false);
// ArrayList<PhoneConnectRuleModel> list = Rules.getInstance(context).getPhoneBlacRuleBeanList();
// list.remove(position);
// Rules.getInstance(context).saveRules();
// notifyItemChanged(position);
// }
// });
// // 触摸事件处理
// simpleViewHolder.contentLayout.setOnTouchListener(new View.OnTouchListener() {
// @Override
// public boolean onTouch(View v, MotionEvent event) {
// switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// simpleViewHolder.startX = event.getX();
// simpleViewHolder.isSwiping = true;
// break;
// case MotionEvent.ACTION_MOVE:
// if (simpleViewHolder.isSwiping) {
// float deltaX = simpleViewHolder.startX - event.getX();
// if (deltaX > 0) { // 左滑
// float translationX = Math.max(-simpleViewHolder.actionLayout.getWidth(), -deltaX);
// simpleViewHolder.contentLayout.setTranslationX(translationX);
// simpleViewHolder.actionLayout.setVisibility(View.VISIBLE);
// }
// }
// break;
// case MotionEvent.ACTION_UP:
// simpleViewHolder.isSwiping = false;
// if (simpleViewHolder.contentLayout.getTranslationX() < -simpleViewHolder.actionLayout.getWidth() / 2) {
// // 保持按钮显示
// simpleViewHolder.contentLayout.setTranslationX(-actionLayout.getWidth());
// } else {
// // 恢复原状
// simpleViewHolder.contentLayout.animate().translationX(0).setDuration(200).start();
// simpleViewHolder.actionLayout.setVisibility(View.INVISIBLE);
// }
// break;
// }
// return true;
// }
// });
} else if (holder instanceof EditViewHolder) {
final EditViewHolder editViewHolder = (EditViewHolder) holder;
editViewHolder.editText.setText(model.getRuleText());
editViewHolder.checkBoxAllow.setChecked(model.isAllowConnection());
editViewHolder.checkBoxEnable.setChecked(model.isEnable());
editViewHolder.buttonConfirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
model.setRuleText(editViewHolder.editText.getText().toString());
model.setIsAllowConnection(editViewHolder.checkBoxAllow.isChecked());
model.setIsEnable(editViewHolder.checkBoxEnable.isChecked());
model.setIsSimpleView(true);
Rules.getInstance(context).saveRules();
notifyItemChanged(position);
Toast.makeText(context, "保存成功", Toast.LENGTH_SHORT).show();
}
});
}
}
@Override
public int getItemCount() {
return ruleList.size();
}
@Override
public int getItemViewType(int position) {
PhoneConnectRuleModel model = ruleList.get(position);
// 这里可以根据模型的状态来决定视图类型简单起见假设点击按钮后进入编辑视图
return model.isSimpleView() ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT;
}
static class SimpleViewHolder extends RecyclerView.ViewHolder {
private final LeftScrollView scrollView;
private final TextView tvRuleText;
public SimpleViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
super(itemView);
scrollView = itemView.findViewById(R.id.scrollView);
//tvRuleText = itemView.findViewById(R.id.ruletext_tv);
tvRuleText = new TextView(itemView.getContext());
scrollView.setContentWidth(parent.getWidth());
//scrollView.setContentWidth(600);
scrollView.addContentLayout(tvRuleText);
}
}
static class EditViewHolder extends RecyclerView.ViewHolder {
EditText editText;
CheckBox checkBoxAllow;
CheckBox checkBoxEnable;
Button buttonConfirm;
public EditViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
super(itemView);
editText = itemView.findViewById(R.id.edit_text);
checkBoxAllow = itemView.findViewById(R.id.checkbox_allow);
checkBoxEnable = itemView.findViewById(R.id.checkbox_enable);
buttonConfirm = itemView.findViewById(R.id.button_confirm);
}
}
}

View File

@ -0,0 +1,36 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:10:57
* @Describe CallLogModel
*/
import java.util.Date;
public class CallLogModel {
public static final String TAG = "CallLogModel";
private String phoneNumber;
private String callStatus;
private Date callDate;
public CallLogModel(String phoneNumber, String callStatus, Date callDate) {
this.phoneNumber = phoneNumber.replaceAll("\\s", "");
this.callStatus = callStatus;
this.callDate = callDate;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getCallStatus() {
return callStatus;
}
public Date getCallDate() {
return callDate;
}
}

View File

@ -0,0 +1,64 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 13:37:00
* @Describe ContactModel
*/
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
public class ContactModel {
public static final String TAG = "ContactModel";
private String name;
private String number;
private String pinyin;
public ContactModel(String name, String number) {
this.name = name;
this.number = number.replaceAll("\\s", "");
this.pinyin = convertToPinyin(name);
}
private String convertToPinyin(String chinese) {
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
StringBuilder pinyin = 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) {
pinyin.append(pinyinArray[0]);
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
pinyin.append(ch);
}
}
return pinyin.toString();
}
public String getName() {
return name;
}
public String getNumber() {
return number;
}
public String getPinyin() {
return pinyin;
}
}

View File

@ -10,21 +10,35 @@ import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean; import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException; import java.io.IOException;
public class PhoneBlackRuleBean extends BaseBean { public class PhoneConnectRuleModel extends BaseBean {
public static final String TAG = "PhoneBlackRuleBean"; public static final String TAG = "PhoneConnectRuleModel";
String ruleText; String ruleText;
boolean isAllowConnection;
boolean isEnable; boolean isEnable;
boolean isSimpleView;
public PhoneBlackRuleBean() {
public PhoneConnectRuleModel() {
this.ruleText = ""; this.ruleText = "";
this.isAllowConnection = false;
this.isEnable = false; this.isEnable = false;
this.isSimpleView = true;
} }
public PhoneBlackRuleBean(String ruleText, boolean isEnable) { public PhoneConnectRuleModel(String ruleText, boolean isAllowConnection, boolean isEnable) {
this.ruleText = ruleText; this.ruleText = ruleText;
this.isAllowConnection = isAllowConnection;
this.isEnable = isEnable; this.isEnable = isEnable;
this.isSimpleView = true;
}
public void setIsSimpleView(boolean isSimpleView) {
this.isSimpleView = isSimpleView;
}
public boolean isSimpleView() {
return isSimpleView;
} }
public void setRuleText(String ruleText) { public void setRuleText(String ruleText) {
@ -35,6 +49,14 @@ public class PhoneBlackRuleBean extends BaseBean {
return ruleText; return ruleText;
} }
public void setIsAllowConnection(boolean isAllowConnection) {
this.isAllowConnection = isAllowConnection;
}
public boolean isAllowConnection() {
return isAllowConnection;
}
public void setIsEnable(boolean isEnable) { public void setIsEnable(boolean isEnable) {
this.isEnable = isEnable; this.isEnable = isEnable;
} }
@ -43,17 +65,19 @@ public class PhoneBlackRuleBean extends BaseBean {
return isEnable; return isEnable;
} }
@Override @Override
public String getName() { public String getName() {
return PhoneBlackRuleBean.class.getName(); return PhoneConnectRuleModel.class.getName();
} }
@Override @Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter); super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("ruleText").value(getRuleText()); jsonWriter.name("ruleText").value(getRuleText());
jsonWriter.name("isAllowConnection").value(isAllowConnection());
jsonWriter.name("isEnable").value(isEnable()); jsonWriter.name("isEnable").value(isEnable());
} }
@Override @Override
@ -61,6 +85,8 @@ public class PhoneBlackRuleBean extends BaseBean {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("ruleText")) { if (name.equals("ruleText")) {
setRuleText(jsonReader.nextString()); setRuleText(jsonReader.nextString());
} else if (name.equals("isAllowConnection")) {
setIsAllowConnection(jsonReader.nextBoolean());
} else if (name.equals("isEnable")) { } else if (name.equals("isEnable")) {
setIsEnable(jsonReader.nextBoolean()); setIsEnable(jsonReader.nextBoolean());
} else { } else {
@ -83,6 +109,6 @@ public class PhoneBlackRuleBean extends BaseBean {
jsonReader.endObject(); jsonReader.endObject();
return this; return this;
} }
} }

View File

@ -12,26 +12,26 @@ import android.media.AudioManager;
import android.util.JsonReader; import android.util.JsonReader;
public class RingTongBean extends BaseBean { public class RingTongBean extends BaseBean {
public static final String TAG = "AudioRingTongBean"; public static final String TAG = "AudioRingTongBean";
// 模式 // 铃声音量
int ringerMode; int streamVolume;
public RingTongBean() { public RingTongBean() {
this.ringerMode = AudioManager.RINGER_MODE_NORMAL; this.streamVolume = 100;
} }
public RingTongBean(int ringerMode) { public RingTongBean(int streamVolume) {
this.ringerMode = ringerMode; this.streamVolume = streamVolume;
} }
public void setRingerMode(int ringerMode) { public void setStreamVolume(int streamVolume) {
this.ringerMode = ringerMode; this.streamVolume = streamVolume;
} }
public int getRingerMode() { public int getStreamVolume() {
return ringerMode; return streamVolume;
} }
@Override @Override
@ -42,15 +42,15 @@ public class RingTongBean extends BaseBean {
@Override @Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter); super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("ringerMode").value(getRingerMode()); jsonWriter.name("streamVolume").value(getStreamVolume());
} }
@Override @Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("ringerMode")) { if (name.equals("streamVolume")) {
setRingerMode(jsonReader.nextInt()); setStreamVolume(jsonReader.nextInt());
} else { } else {
return false; return false;
} }

View File

@ -0,0 +1,141 @@
package cc.winboll.studio.contacts.beans;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 19:51:40
* @Describe SettingsModel
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
import cc.winboll.studio.contacts.utils.IntUtils;
public class SettingsModel extends BaseBean {
public static final String TAG = "SettingsModel";
public static final int MAX_INTRANGE = 666666;
public static final int MIN_INTRANGE = 1;
// 云盾防御层数量
int dunTotalCount;
// 当前云盾防御层
int dunCurrentCount;
// 防御层恢复时间间隔(秒钟)
int dunResumeSecondCount;
// 每次恢复防御层数
int dunResumeCount;
// 是否启用云盾
boolean isEnableDun;
public SettingsModel() {
this.dunTotalCount = 6;
this.dunCurrentCount = 6;
this.dunResumeSecondCount = 60;
this.dunResumeCount = 1;
this.isEnableDun = false;
}
public SettingsModel(int dunTotalCount, int dunCurrentCount, int dunResumeSecondCount, int dunResumeCount, boolean isEnableDun) {
this.dunTotalCount = getSettingsModelRangeInt(dunTotalCount);
this.dunCurrentCount = getSettingsModelRangeInt(dunCurrentCount);
this.dunResumeSecondCount = getSettingsModelRangeInt(dunResumeSecondCount);
this.dunResumeCount = getSettingsModelRangeInt(dunResumeCount);
this.isEnableDun = isEnableDun;
}
public void setDunTotalCount(int dunTotalCount) {
this.dunTotalCount = getSettingsModelRangeInt(dunTotalCount);
}
public int getDunTotalCount() {
return dunTotalCount;
}
public void setDunCurrentCount(int dunCurrentCount) {
this.dunCurrentCount = getSettingsModelRangeInt(dunCurrentCount);
}
public int getDunCurrentCount() {
return dunCurrentCount;
}
public void setDunResumeSecondCount(int dunResumeSecondCount) {
this.dunResumeSecondCount = getSettingsModelRangeInt(dunResumeSecondCount);
}
public int getDunResumeSecondCount() {
return dunResumeSecondCount;
}
public void setDunResumeCount(int dunResumeCount) {
this.dunResumeCount = getSettingsModelRangeInt(dunResumeCount);
}
public int getDunResumeCount() {
return dunResumeCount;
}
public void setIsEnableDun(boolean isEnableDun) {
this.isEnableDun = isEnableDun;
}
public boolean isEnableDun() {
return isEnableDun;
}
int getSettingsModelRangeInt(int origin) {
return IntUtils.getIntInRange(origin, MIN_INTRANGE, MAX_INTRANGE);
}
@Override
public String getName() {
return SettingsModel.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("dunTotalCount").value(getDunTotalCount());
jsonWriter.name("dunCurrentCount").value(getDunCurrentCount());
jsonWriter.name("dunResumeSecondCount").value(getDunResumeSecondCount());
jsonWriter.name("dunResumeCount").value(getDunResumeCount());
jsonWriter.name("isEnableDun").value(isEnableDun());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("dunTotalCount")) {
setDunTotalCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("dunCurrentCount")) {
setDunCurrentCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("dunResumeSecondCount")) {
setDunResumeSecondCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("dunResumeCount")) {
setDunResumeCount(getSettingsModelRangeInt(jsonReader.nextInt()));
} else if (name.equals("isEnableDun")) {
setIsEnableDun(jsonReader.nextBoolean());
} else {
return false;
}
}
return true;
}
@Override
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (!initObjectsFromJsonReader(jsonReader, name)) {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return this;
}
}

View File

@ -0,0 +1,178 @@
package cc.winboll.studio.contacts.bobulltoon;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 13:47:48
* @Describe 汤姆猫管家 使用 BoBullToon 项目对通讯地址进行筛选判断的好朋友
*/
import android.content.Context;
import cc.winboll.studio.libappbase.LogUtils;
import com.hjq.toast.ToastUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class TomCat {
public static final String TAG = "TomCat";
List<String> listPhoneBoBullToon = new ArrayList<String>();
static volatile TomCat _TomCat;
Context mContext;
TomCat(Context context) {
mContext = context;
}
public static synchronized TomCat getInstance(Context context) {
if (_TomCat == null) {
_TomCat = new TomCat(context);
}
return _TomCat;
}
void downloadAndExtractZip(String zipUrl, String destinationFolder) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(zipUrl)
.build();
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
// 下载 ZIP 文件到临时位置
File tempZipFile = File.createTempFile("temp", ".zip");
try {
InputStream inputStream = response.body().byteStream();
FileOutputStream outputStream = new FileOutputStream(tempZipFile);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 解压 ZIP 文件到指定文件夹
try {
ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(tempZipFile.toPath()));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
Path targetFilePath = Paths.get(destinationFolder, zipEntry.getName());
if (zipEntry.isDirectory()) {
Files.createDirectories(targetFilePath);
} else {
Files.createDirectories(targetFilePath.getParent());
try (FileOutputStream fos = new FileOutputStream(targetFilePath.toFile())) {
byte[] buffer = new byte[1024];
int len;
while ((len = zipInputStream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
}
zipInputStream.closeEntry();
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 删除临时 ZIP 文件
tempZipFile.delete();
LogUtils.d(TAG, "已更新 BoBullToon 数据");
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
public boolean downloadBoBullToon() {
String zipUrl = "http://10.8.0.12:3000/Studio/BoBullToon/archive/main.zip"; // 替换为实际的 ZIP 文件 URL
String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径
try {
// 删除旧文件
File fOldFolder = new File(destinationFolder);
if (fOldFolder.exists()) {
deleteFolderRecursive(fOldFolder);
fOldFolder.mkdirs();
LogUtils.d(TAG, "已清空 BoBullToon 数据");
}
// 更新新文件
downloadAndExtractZip(zipUrl, destinationFolder);
LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
return true;
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
return false;
}
// 递归删除文件夹及其内容的方法
public static void deleteFolderRecursive(File file) {
// 判断是否为文件夹
if (file.isDirectory()) {
// 列出文件夹中的所有文件和子文件夹
File[] files = file.listFiles();
if (files != null) {
// 遍历并递归删除每个文件和子文件夹
for (File f : files) {
deleteFolderRecursive(f);
}
}
}
// 删除文件或空文件夹
file.delete();
}
File getWorkingFolder() {
return mContext.getExternalFilesDir(TAG);
}
public boolean loadPhoneBoBullToon() {
listPhoneBoBullToon.clear();
File fBoBullToon = new File(getWorkingFolder(), "bobulltoon");
if (fBoBullToon.exists()) {
LogUtils.d(TAG, String.format("getWorkingFolder() %s", getWorkingFolder()));
for (File userFolder : fBoBullToon.listFiles()) {
if (userFolder.isDirectory()) {
for (File recordFile : userFolder.listFiles()) {
listPhoneBoBullToon.add(recordFile.getName());
}
}
}
for (int i = 0; i < listPhoneBoBullToon.size(); i++) {
LogUtils.d(TAG, String.format("listPhoneBoBullToon add : %s", listPhoneBoBullToon.get(i)));
}
return true;
} else {
LogUtils.d(TAG, "fBoBullToon not exists。");
}
return false;
}
public boolean isPhoneBoBullToon(String phone) {
for (int i = 0; i < listPhoneBoBullToon.size(); i++) {
LogUtils.d(TAG, String.format("isPhoneBoBullToon(...) get(i) phone : %s", listPhoneBoBullToon.get(i)));
if (listPhoneBoBullToon.get(i).equals(phone)) {
return true;
}
}
return false;
}
}

View File

@ -5,25 +5,36 @@ package cc.winboll.studio.contacts.dun;
* @Date 2025/02/21 06:15:10 * @Date 2025/02/21 06:15:10
* @Describe 云盾防御规则 * @Describe 云盾防御规则
*/ */
import cc.winboll.studio.contacts.beans.PhoneBlackRuleBean;
import java.util.ArrayList;
import java.util.regex.Pattern;
import android.content.Context; import android.content.Context;
import cc.winboll.studio.contacts.activities.SettingsActivity;
import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
import cc.winboll.studio.contacts.beans.SettingsModel;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.utils.ContactUtils;
import cc.winboll.studio.contacts.utils.IntUtils;
import cc.winboll.studio.contacts.utils.RegexPPiUtils;
import cc.winboll.studio.libappbase.LogUtils;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;
public class Rules { public class Rules {
public static final String TAG = "Rules"; public static final String TAG = "Rules";
ArrayList<PhoneBlackRuleBean> _PhoneBlacRuleBeanList; ArrayList<PhoneConnectRuleModel> _PhoneConnectRuleModelList;
static volatile Rules _Rules; static volatile Rules _Rules;
Context mContext; Context mContext;
SettingsModel mSettingsModel;
Timer mDunResumeTimer;
Rules(Context context) { Rules(Context context) {
mContext = context; mContext = context;
_PhoneBlacRuleBeanList = new ArrayList<PhoneBlackRuleBean>(); _PhoneConnectRuleModelList = new ArrayList<PhoneConnectRuleModel>();
PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class); reload();
} }
public static synchronized Rules getInstance(Context context) { public static synchronized Rules getInstance(Context context) {
if (_Rules == null) { if (_Rules == null) {
_Rules = new Rules(context); _Rules = new Rules(context);
@ -31,46 +42,168 @@ public class Rules {
return _Rules; return _Rules;
} }
public void reload() {
LogUtils.d(TAG, "reload()");
loadRules();
loadDun();
setDunResumTimer();
}
public void setDunResumTimer() {
if (mDunResumeTimer != null) {
mDunResumeTimer.cancel();
}
// 盾牌恢复定时器
mDunResumeTimer = new Timer();
int ss = IntUtils.getIntInRange(mSettingsModel.getDunResumeSecondCount() * 1000, SettingsModel.MIN_INTRANGE, SettingsModel.MAX_INTRANGE);
mDunResumeTimer.schedule(new TimerTask() {
@Override
public void run() {
if (mSettingsModel.getDunCurrentCount() != mSettingsModel.getDunTotalCount()) {
LogUtils.d(TAG, String.format("当前防御值为%d最大防御值为%d", mSettingsModel.getDunCurrentCount(), mSettingsModel.getDunTotalCount()));
int newDunCount = mSettingsModel.getDunCurrentCount() + mSettingsModel.getDunResumeCount();
// 设置盾值在[0DunTotalCount]之内其他值一律重置为 DunTotalCount
newDunCount = (newDunCount > mSettingsModel.getDunTotalCount()) ?mSettingsModel.getDunTotalCount(): newDunCount;
mSettingsModel.setDunCurrentCount(newDunCount);
LogUtils.d(TAG, String.format("设置防御值为%d", newDunCount));
saveDun();
SettingsActivity.notifyDunInfoUpdate();
}
}
}, 1000, ss);
}
public void loadRules() {
_PhoneConnectRuleModelList.clear();
PhoneConnectRuleModel.loadBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class);
}
public void saveRules() {
LogUtils.d(TAG, String.format("saveRules()"));
PhoneConnectRuleModel.saveBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class);
}
public void loadDun() {
mSettingsModel = SettingsModel.loadBean(mContext, SettingsModel.class);
if (mSettingsModel == null) {
mSettingsModel = new SettingsModel();
SettingsModel.saveBean(mContext, mSettingsModel);
}
}
public void saveDun() {
LogUtils.d(TAG, String.format("saveDun()"));
SettingsModel.saveBean(mContext, mSettingsModel);
}
public boolean isAllowed(String phoneNumber) { public boolean isAllowed(String phoneNumber) {
// 黑名单拒接 // 没有启用云盾默认允许接通任何电话
for (int i = 0; i < _PhoneBlacRuleBeanList.size(); i++) { if (!mSettingsModel.isEnableDun()) {
if (_PhoneBlacRuleBeanList.get(i).isEnable()) { LogUtils.d(TAG, String.format("没有启用云盾默认允许接通任何电话。isAllowed(...) return true"));
String regex = _PhoneBlacRuleBeanList.get(i).getRuleText(); return true;
if (Pattern.matches(regex, phoneNumber)) { }
return false;
//
// 以下是云盾防御体系
boolean isDefend = false; // 盾牌是否生效
boolean isConnect = true; // 防御结果是否连接
// 如果盾值小于1则解除防御
if (!isDefend && mSettingsModel.getDunCurrentCount() < 1) {
// 盾层为1以下防御解除
LogUtils.d(TAG, "盾层为1以下防御解除");
isDefend = true;
isConnect = true;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 正则运算预防针
if (!isDefend && !RegexPPiUtils.isPPiOK(phoneNumber)) {
LogUtils.d(TAG, "正则运算预防针生效。");
isDefend = true;
isConnect = false;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 查询通讯录是否有该联系人
boolean isPhoneInContacts = ContactUtils.getInstance(mContext).isPhoneInContacts(mContext, phoneNumber);
if (!isDefend) {
if (isPhoneInContacts) {
LogUtils.d(TAG, String.format("Phone %s is in contacts.", phoneNumber));
isDefend = true;
isConnect = true;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
} else {
LogUtils.d(TAG, String.format("Phone %s is not in contacts.", phoneNumber));
}
}
// 检验拨不通号码群
if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
isDefend = true;
isConnect = false;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
// 正则匹配规则名单校验
if (!isDefend) {
for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) {
if (_PhoneConnectRuleModelList.get(i).isEnable()) {
String regex = _PhoneConnectRuleModelList.get(i).getRuleText();
if (Pattern.matches(regex, phoneNumber)) {
LogUtils.d(TAG, String.format("Phone Number [%s] is matched by rule : %s", phoneNumber, _PhoneConnectRuleModelList.get(i)));
isDefend = true;
isConnect = _PhoneConnectRuleModelList.get(i).isAllowConnection();
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
break;
}
} }
} }
} }
// 手机号码允许 if (isConnect) {
// 中国手机号码正则表达式以1开头第二位可以是3456789后面跟9位数字 // 如果防御结果为连接则恢复防御盾牌最大值层数
String regex = "^1[3-9]\\d{9}$"; mSettingsModel.setDunCurrentCount(mSettingsModel.getDunTotalCount());
if (Pattern.matches(regex, phoneNumber)) { LogUtils.d(TAG, String.format("防御结果为连接,恢复防御盾牌最大值层数 %d", mSettingsModel.getDunTotalCount()));
return true; saveDun();
} SettingsActivity.notifyDunInfoUpdate();
} else if (isDefend) {
// 指定区号号码允许 // 如果触发了以上某个防御模块
regex = "^0660\\d+$"; // 就减少防御盾牌层数
if (Pattern.matches(regex, phoneNumber)) { // 每校验一次规则云盾防御层数减1
return true; // 当云盾防御层数为0时再次进行以下程序段则恢复满值防御
} int newDunCount = mSettingsModel.getDunCurrentCount() - 1;
LogUtils.d(TAG, String.format("新的防御层数预计为 %d", newDunCount));
// 指定区号号码允许
regex = "^020\\d+$"; // 保证盾值在[0DunTotalCount]之内其他值一律重置为 DunTotalCount
if (Pattern.matches(regex, phoneNumber)) { if (newDunCount < 0 || newDunCount > mSettingsModel.getDunTotalCount()) {
return true; mSettingsModel.setDunCurrentCount(mSettingsModel.getDunTotalCount());
LogUtils.d(TAG, String.format("盾值不在[0%d]区间,恢复防御最大值%d", mSettingsModel.getDunTotalCount(), mSettingsModel.getDunTotalCount()));
} else {
mSettingsModel.setDunCurrentCount(newDunCount);
LogUtils.d(TAG, String.format("设置防御层数为 %d", newDunCount));
}
saveDun();
SettingsActivity.notifyDunInfoUpdate();
} }
// 其他拒接 // 返回校验结果
return false; LogUtils.d(TAG, String.format("返回校验结果 isConnect == %s", isConnect));
return isConnect;
} }
public void add(String phoneRuleBlack, boolean isEnable) { public void add(String szPhoneConnectRule, boolean isAllowConnection, boolean isEnable) {
_PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable)); _PhoneConnectRuleModelList.add(new PhoneConnectRuleModel(szPhoneConnectRule, isAllowConnection, isEnable));
PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
} }
public ArrayList<PhoneBlackRuleBean> getPhoneBlacRuleBeanList() { public ArrayList<PhoneConnectRuleModel> getPhoneBlacRuleBeanList() {
return _PhoneBlacRuleBeanList; return _PhoneConnectRuleModelList;
}
public SettingsModel getSettingsModel() {
return mSettingsModel;
} }
} }

View File

@ -1,51 +0,0 @@
package cc.winboll.studio.contacts.fragments;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 12:57:00
* @Describe 拨号
*/
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libappbase.LogView;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import android.widget.TextView;
public class CallFragment extends Fragment {
public static final String TAG = "CallFragment";
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
public static CallFragment newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
CallFragment fragment = new CallFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments()!= null) {
mPage = getArguments().getInt(ARG_PAGE);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_call, container, false);
TextView textView = view.findViewById(R.id.page_text);
textView.setText("这是第 " + mPage + "");
return view;
}
}

View File

@ -0,0 +1,164 @@
package cc.winboll.studio.contacts.fragments;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/20 12:57:00
* @Describe 拨号
*/
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.CallLog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.adapters.CallLogAdapter;
import cc.winboll.studio.contacts.beans.CallLogModel;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CallLogFragment extends Fragment {
public static final String TAG = "CallFragment";
static volatile CallLogFragment _CallLogFragment;
public static final int MSG_UPDATE = 1; // 添加消息常量
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
private static final int REQUEST_READ_CALL_LOG = 1;
private RecyclerView recyclerView;
private CallLogAdapter callLogAdapter;
private List<CallLogModel> callLogList = new ArrayList<>();
// 添加Handler
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == MSG_UPDATE) {
readCallLog(); // 接收到消息时更新通话记录
}
}
};
CallLogFragment() {
super();
}
public static CallLogFragment newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
CallLogFragment fragment = new CallLogFragment();
fragment.setArguments(args);
_CallLogFragment = fragment;
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_call_log, container, false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mPage = getArguments().getInt(ARG_PAGE);
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
callLogAdapter = new CallLogAdapter(getContext(), callLogList);
recyclerView.setAdapter(callLogAdapter);
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CALL_LOG}, REQUEST_READ_CALL_LOG);
} else {
mHandler.sendEmptyMessage(MSG_UPDATE); // 通过Handler触发更新
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_READ_CALL_LOG) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mHandler.sendEmptyMessage(MSG_UPDATE); // 通过Handler触发更新
}
}
}
private void readCallLog() {
callLogList.clear(); // 清空原有数据
Cursor cursor = requireContext().getContentResolver().query(
CallLog.Calls.CONTENT_URI,
null,
null,
null,
CallLog.Calls.DATE + " DESC");
if (cursor != null) {
while (cursor.moveToNext()) {
String phoneNumber = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
int callType = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE));
long callDateLong = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
Date callDate = new Date(callDateLong);
String callStatus = getCallStatus(callType);
callLogList.add(new CallLogModel(phoneNumber, callStatus, callDate));
}
cursor.close();
callLogAdapter.notifyDataSetChanged();
}
}
private String getCallStatus(int callType) {
switch (callType) {
case CallLog.Calls.OUTGOING_TYPE:
return "Outgoing";
case CallLog.Calls.INCOMING_TYPE:
return "Incoming";
case CallLog.Calls.MISSED_TYPE:
return "Missed";
default:
return "Unknown";
}
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null); // 清理Handler防止内存泄漏
}
public void triggerUpdate() {
mHandler.sendEmptyMessage(MSG_UPDATE);
}
public static void updateCallLogFragment() {
if (_CallLogFragment != null) {
_CallLogFragment.triggerUpdate();
}
}
}

View File

@ -5,23 +5,47 @@ package cc.winboll.studio.contacts.fragments;
* @Date 2025/02/20 12:57:50 * @Date 2025/02/20 12:57:50
* @Describe 联系人 * @Describe 联系人
*/ */
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.Button;
import android.widget.EditText;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.adapters.ContactAdapter;
import cc.winboll.studio.contacts.beans.ContactModel;
import com.hjq.toast.ToastUtils;
import java.util.ArrayList;
import java.util.List;
public class ContactsFragment extends Fragment { public class ContactsFragment extends Fragment {
public static final String TAG = "ContactsFragment"; public static final String TAG = "ContactsFragment";
private static final String ARG_PAGE = "ARG_PAGE"; private static final String ARG_PAGE = "ARG_PAGE";
private int mPage; private int mPage;
private static final int REQUEST_READ_CONTACTS = 1;
private RecyclerView recyclerView;
private ContactAdapter contactAdapter;
private List<ContactModel> contactList = new ArrayList<>();
private List<ContactModel> originalContactList = new ArrayList<>();
private EditText searchEditText;
public static ContactsFragment newInstance(int page) { public static ContactsFragment newInstance(int page) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt(ARG_PAGE, page); args.putInt(ARG_PAGE, page);
@ -33,18 +57,111 @@ public class ContactsFragment extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (getArguments()!= null) { if (getArguments() != null) {
mPage = getArguments().getInt(ARG_PAGE); mPage = getArguments().getInt(ARG_PAGE);
} }
} }
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_contacts, container, false);
View view = inflater.inflate(R.layout.fragment_contacts, container, false); }
TextView textView = view.findViewById(R.id.page_text);
textView.setText("这是第 " + mPage + ""); @Override
return view; public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.contacts_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
contactAdapter = new ContactAdapter(contactList);
recyclerView.setAdapter(contactAdapter);
searchEditText = view.findViewById(R.id.search_edit_text);
searchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
filterContacts(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
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();
}
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);
}
});
}
@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();
}
}
}
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");
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();
}
}
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);
}
}
}
contactAdapter.notifyDataSetChanged();
} }
} }

View File

@ -14,6 +14,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libappbase.LogView; import cc.winboll.studio.libappbase.LogView;
import com.hjq.toast.ToastUtils;
public class LogFragment extends Fragment { public class LogFragment extends Fragment {
@ -21,6 +22,8 @@ public class LogFragment extends Fragment {
private static final String ARG_PAGE = "ARG_PAGE"; private static final String ARG_PAGE = "ARG_PAGE";
private int mPage; private int mPage;
LogView mLogView;
public static LogFragment newInstance(int page) { public static LogFragment newInstance(int page) {
Bundle args = new Bundle(); Bundle args = new Bundle();
@ -43,8 +46,17 @@ public class LogFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_log, container, false); View view = inflater.inflate(R.layout.fragment_log, container, false);
LogView logView = view.findViewById(R.id.logview); mLogView = view.findViewById(R.id.logview);
logView.start(); mLogView.start();
return view; return view;
} }
@Override
public void onResume() {
super.onResume();
//ToastUtils.show("onResume");
mLogView.start();
}
} }

View File

@ -21,6 +21,8 @@ import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import cc.winboll.studio.contacts.MainActivity; import cc.winboll.studio.contacts.MainActivity;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.phonecallui.PhoneCallActivity;
import cc.winboll.studio.contacts.phonecallui.PhoneCallService;
public class CallListenerService extends Service { public class CallListenerService extends Service {
@ -152,9 +154,12 @@ public class CallListenerService extends Service {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class); // Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
CallListenerService.this.startActivity(intent); // CallListenerService.this.startActivity(intent);
PhoneCallService.CallType callType = isCallingIn ? PhoneCallService.CallType.CALL_IN: PhoneCallService.CallType.CALL_OUT;
PhoneCallActivity.actionStart(CallListenerService.this, callNumber, callType);
} }
}); });
} }

View File

@ -1,7 +1,5 @@
package cc.winboll.studio.contacts.phonecallui; package cc.winboll.studio.contacts.phonecallui;
import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -10,16 +8,16 @@ import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.contacts.ActivityStack; import cc.winboll.studio.contacts.ActivityStack;
import cc.winboll.studio.contacts.MainActivity;
import cc.winboll.studio.contacts.R; import cc.winboll.studio.contacts.R;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
/** /**
* 提供接打电话的界面仅支持 Android M (6.0, API 23) 及以上的系统 * 提供接打电话的界面仅支持 Android M (6.0, API 23) 及以上的系统
@ -57,10 +55,9 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
setContentView(R.layout.activity_phone_call); setContentView(R.layout.activity_phone_call);
ActivityStack.getInstance().addActivity(this); ActivityStack.getInstance().addActivity(this);
initData(); initData();
initView(); initView();
} }
private void initData() { private void initData() {
@ -74,9 +71,9 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
private void initView() { private void initView() {
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE; | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
getWindow().getDecorView().setSystemUiVisibility(uiOptions); getWindow().getDecorView().setSystemUiVisibility(uiOptions);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
@ -94,9 +91,7 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
if (callType == PhoneCallService.CallType.CALL_IN) { if (callType == PhoneCallService.CallType.CALL_IN) {
tvCallNumberLabel.setText("来电号码"); tvCallNumberLabel.setText("来电号码");
tvPickUp.setVisibility(View.VISIBLE); tvPickUp.setVisibility(View.VISIBLE);
} } else if (callType == PhoneCallService.CallType.CALL_OUT) {
// 打出的电话
else if (callType == PhoneCallService.CallType.CALL_OUT) {
tvCallNumberLabel.setText("呼叫号码"); tvCallNumberLabel.setText("呼叫号码");
tvPickUp.setVisibility(View.GONE); tvPickUp.setVisibility(View.GONE);
phoneCallManager.openSpeaker(); phoneCallManager.openSpeaker();
@ -107,13 +102,13 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
public void showOnLockScreen() { public void showOnLockScreen() {
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON, WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
} }
@Override @Override
@ -123,18 +118,18 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
tvPickUp.setVisibility(View.GONE); tvPickUp.setVisibility(View.GONE);
tvCallingTime.setVisibility(View.VISIBLE); tvCallingTime.setVisibility(View.VISIBLE);
onGoingCallTimer.schedule(new TimerTask() { onGoingCallTimer.schedule(new TimerTask() {
@Override @Override
public void run() { public void run() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Override @Override
public void run() { public void run() {
callingTime++; callingTime++;
tvCallingTime.setText("通话中:" + getCallingTime()); tvCallingTime.setText("通话中:" + getCallingTime());
} }
}); });
} }
}, 0, 1000); }, 0, 1000);
} else if (v.getId() == R.id.tv_phone_hang_up) { } else if (v.getId() == R.id.tv_phone_hang_up) {
phoneCallManager.disconnect(); phoneCallManager.disconnect();
stopTimer(); stopTimer();
@ -145,8 +140,8 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
int minute = callingTime / 60; int minute = callingTime / 60;
int second = callingTime % 60; int second = callingTime % 60;
return (minute < 10 ? "0" + minute : minute) + return (minute < 10 ? "0" + minute : minute) +
":" + ":" +
(second < 10 ? "0" + second : second); (second < 10 ? "0" + second : second);
} }
private void stopTimer() { private void stopTimer() {
@ -160,7 +155,7 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
//MainActivity.updateCallLogFragment();
phoneCallManager.destroy(); phoneCallManager.destroy();
} }
} }

View File

@ -6,7 +6,7 @@ import android.os.Build;
import android.telecom.Call; import android.telecom.Call;
import android.telecom.VideoProfile; import android.telecom.VideoProfile;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import cc.winboll.studio.contacts.dun.Rules; import cc.winboll.studio.contacts.MainActivity;
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)

View File

@ -7,28 +7,53 @@ package cc.winboll.studio.contacts.phonecallui;
* @see PhoneCallActivity * @see PhoneCallActivity
* @see android.telecom.InCallService * @see android.telecom.InCallService
*/ */
import android.content.ContentResolver;
import android.database.Cursor;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.provider.CallLog;
import android.telecom.Call; import android.telecom.Call;
import android.telecom.InCallService; import android.telecom.InCallService;
import android.telephony.TelephonyManager;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import cc.winboll.studio.contacts.ActivityStack; import cc.winboll.studio.contacts.ActivityStack;
import cc.winboll.studio.contacts.beans.RingTongBean; import cc.winboll.studio.contacts.beans.RingTongBean;
import cc.winboll.studio.contacts.dun.Rules; import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.fragments.CallLogFragment;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import java.io.File;
import java.io.IOException;
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)
public class PhoneCallService extends InCallService { public class PhoneCallService extends InCallService {
public static final String TAG = "PhoneCallService"; public static final String TAG = "PhoneCallService";
private volatile int originalRingVolume; MediaRecorder mediaRecorder;
private final Call.Callback callback = new Call.Callback() { private final Call.Callback callback = new Call.Callback() {
@Override @Override
public void onStateChanged(Call call, int state) { public void onStateChanged(Call call, int state) {
super.onStateChanged(call, state); super.onStateChanged(call, state);
switch (state) { switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
{
long callId = getCurrentCallId();
if (callId != -1) {
// 在这里可以对获取到的通话记录ID进行处理
//System.out.println("当前通话记录ID: " + callId);
// 电话接通开始录音
startRecording(callId);
}
break;
}
case TelephonyManager.CALL_STATE_IDLE:
// 电话挂断停止录音
stopRecording();
break;
case Call.STATE_ACTIVE: { case Call.STATE_ACTIVE: {
break; break;
} }
@ -61,25 +86,58 @@ public class PhoneCallService extends InCallService {
String phoneNumber = details.getHandle().getSchemeSpecificPart(); String phoneNumber = details.getHandle().getSchemeSpecificPart();
// 记录原始铃声音量 // 记录原始铃声音量
//
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
originalRingVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); int ringerVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
// 恢复铃声音量预防其他意外条件导致的音量变化问题
//
// 读取应用配置未配置就初始化配置文件
RingTongBean bean = RingTongBean.loadBean(this, RingTongBean.class);
if (bean == null) {
// 初始化配置
bean = new RingTongBean();
RingTongBean.saveBean(this, bean);
}
// 如果当前音量和应用保存的不一致就恢复为应用设定值
// 恢复铃声音量
try {
if (ringerVolume != bean.getStreamVolume()) {
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
}
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 检查电话接收规则 // 检查电话接收规则
if (!Rules.getInstance(this).isAllowed(phoneNumber)) { if (!Rules.getInstance(this).isAllowed(phoneNumber)) {
// 预先静音 // 调低音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0); try {
audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
//audioManager.setMode(AudioManager.RINGER_MODE_SILENT);
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 断开电话 // 断开电话
call.disconnect(); call.disconnect();
// 停顿1秒预防第一声铃声响动 // 停顿1秒预防第一声铃声响动
try { try {
Thread.sleep(1000); Thread.sleep(500);
} catch (InterruptedException e) { } catch (InterruptedException e) {
LogUtils.d(TAG, ""); LogUtils.d(TAG, "");
} }
// 恢复铃声音量 // 恢复铃声音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0); try {
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
// 屏蔽电话结束 // 屏蔽电话结束
return; return;
} }
// 正常接听电话 // 正常接听电话
PhoneCallActivity.actionStart(this, phoneNumber, callType); PhoneCallActivity.actionStart(this, phoneNumber, callType);
} }
@ -88,16 +146,70 @@ public class PhoneCallService extends InCallService {
@Override @Override
public void onCallRemoved(Call call) { public void onCallRemoved(Call call) {
super.onCallRemoved(call); super.onCallRemoved(call);
call.unregisterCallback(callback); call.unregisterCallback(callback);
PhoneCallManager.call = null; PhoneCallManager.call = null;
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); }
// 恢复铃声音量
audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0); @Override
public void onDestroy() {
super.onDestroy();
CallLogFragment.updateCallLogFragment();
} }
public enum CallType { public enum CallType {
CALL_IN, CALL_IN,
CALL_OUT, CALL_OUT,
} }
private void startRecording(long callId) {
LogUtils.d(TAG, "startRecording(...)");
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mediaRecorder.setOutputFile(getOutputFilePath(callId));
try {
mediaRecorder.prepare();
mediaRecorder.start();
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
private String getOutputFilePath(long callId) {
LogUtils.d(TAG, "getOutputFilePath(...)");
// 设置录音文件的保存路径
File file = new File(getExternalFilesDir(TAG), String.format("call_%d.mp4", callId));
return file.getAbsolutePath();
}
private void stopRecording() {
LogUtils.d(TAG, "stopRecording()");
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
}
}
private long getCurrentCallId() {
LogUtils.d(TAG, "getCurrentCallId()");
ContentResolver contentResolver = getApplicationContext().getContentResolver();
Uri callLogUri = Uri.parse("content://call_log/calls");
String[] projection = {"_id", "number", "call_type", "date"};
String selection = "call_type = " + CallLog.Calls.OUTGOING_TYPE + " OR call_type = " + CallLog.Calls.INCOMING_TYPE;
String sortOrder = "date DESC";
try {
Cursor cursor = contentResolver.query(callLogUri, projection, selection, null, sortOrder);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndex("_id"));
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
return -1;
}
} }

View File

@ -9,13 +9,9 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.media.RingtoneManager;
import android.net.Uri;
import android.util.Log;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import cc.winboll.studio.libappbase.LogUtils;
public class MainReceiver extends BroadcastReceiver { public class MainReceiver extends BroadcastReceiver {
@ -43,7 +39,7 @@ public class MainReceiver extends BroadcastReceiver {
public void registerAction(Context context) { public void registerAction(Context context) {
IntentFilter filter=new IntentFilter(); IntentFilter filter=new IntentFilter();
filter.addAction(ACTION_BOOT_COMPLETED); filter.addAction(ACTION_BOOT_COMPLETED);
//filter.addAction(Intent.ACTION_BATTERY_CHANGED); //filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
context.registerReceiver(this, filter); context.registerReceiver(this, filter);
} }
} }

View File

@ -15,8 +15,6 @@ import android.os.IBinder;
import cc.winboll.studio.contacts.beans.MainServiceBean; import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.SOS;
import cc.winboll.studio.libappbase.bean.APPSOSBean;
public class AssistantService extends Service { public class AssistantService extends Service {

View File

@ -11,25 +11,29 @@ package cc.winboll.studio.contacts.services;
* https://blog.csdn.net/cyp331203/article/details/38920491 * https://blog.csdn.net/cyp331203/article/details/38920491
*/ */
import android.app.Service; import android.app.Service;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import cc.winboll.studio.contacts.beans.MainServiceBean; import cc.winboll.studio.contacts.beans.MainServiceBean;
import cc.winboll.studio.contacts.beans.RingTongBean;
import cc.winboll.studio.contacts.bobulltoon.TomCat;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.contacts.handlers.MainServiceHandler; import cc.winboll.studio.contacts.handlers.MainServiceHandler;
import cc.winboll.studio.contacts.listenphonecall.CallListenerService;
import cc.winboll.studio.contacts.receivers.MainReceiver; import cc.winboll.studio.contacts.receivers.MainReceiver;
import cc.winboll.studio.contacts.services.MainService; import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.threads.MainServiceThread; import cc.winboll.studio.contacts.threads.MainServiceThread;
import cc.winboll.studio.contacts.widgets.APPStatusWidget;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.SOS; import cc.winboll.studio.libappbase.sos.SOS;
import cc.winboll.studio.libappbase.bean.APPSOSBean; import java.util.Timer;
import cc.winboll.studio.contacts.dun.Rules; import java.util.TimerTask;
import android.media.AudioManager; import cc.winboll.studio.libappbase.sos.WinBoll;
import com.hjq.toast.ToastUtils; import cc.winboll.studio.contacts.App;
import cc.winboll.studio.libappbase.sos.APPModel;
public class MainService extends Service { public class MainService extends Service {
@ -48,8 +52,9 @@ public class MainService extends Service {
AssistantService mAssistantService; AssistantService mAssistantService;
boolean isBound = false; boolean isBound = false;
MainReceiver mMainReceiver; MainReceiver mMainReceiver;
Timer mStreamVolumeCheckTimer;
static volatile TomCat _TomCat;
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return new MyBinder(); return new MyBinder();
@ -71,9 +76,37 @@ public class MainService extends Service {
mMyServiceConnection = new MyServiceConnection(); mMyServiceConnection = new MyServiceConnection();
} }
mMainServiceHandler = new MainServiceHandler(this); mMainServiceHandler = new MainServiceHandler(this);
// 铃声检查定时器
mStreamVolumeCheckTimer = new Timer();
mStreamVolumeCheckTimer.schedule(new TimerTask() {
@Override
public void run() {
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int ringerVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
// 恢复铃声音量预防其他意外条件导致的音量变化问题
//
// 读取应用配置未配置就初始化配置文件
RingTongBean bean = RingTongBean.loadBean(MainService.this, RingTongBean.class);
if (bean == null) {
// 初始化配置
bean = new RingTongBean();
RingTongBean.saveBean(MainService.this, bean);
}
// 如果当前音量和应用保存的不一致就恢复为应用设定值
// 恢复铃声音量
try {
if (ringerVolume != bean.getStreamVolume()) {
audioManager.setStreamVolume(AudioManager.STREAM_RING, bean.getStreamVolume(), 0);
//audioManager.setMode(AudioManager.RINGER_MODE_NORMAL);
}
} catch (java.lang.SecurityException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
}, 1000, 60000);
// 运行服务内容 // 运行服务内容
mainService(); mainService();
} }
@ -97,18 +130,26 @@ public class MainService extends Service {
// 唤醒守护进程 // 唤醒守护进程
wakeupAndBindAssistant(); wakeupAndBindAssistant();
// 召唤 WinBoll APP 绑定本服务 // 召唤 WinBoll APP 绑定本服务
SOS.bindToAPPService(this, new APPSOSBean(getPackageName(), MainService.class.getName())); if (App.isDebuging()) {
WinBoll.bindToAPPBaseBeta(this, MainService.class.getName());
} else {
WinBoll.bindToAPPBase(this, MainService.class.getName());
}
// 初始化服务运行参数
_TomCat = TomCat.getInstance(this);
if (!_TomCat.loadPhoneBoBullToon()) {
LogUtils.d(TAG, "没有下载 BoBullToon 数据。BoBullToon 参数无法加载。");
}
if (mMainReceiver == null) { if (mMainReceiver == null) {
// 注册广播接收器 // 注册广播接收器
mMainReceiver = new MainReceiver(this); mMainReceiver = new MainReceiver(this);
mMainReceiver.registerAction(this); mMainReceiver.registerAction(this);
} }
Rules.getInstance(this); Rules.getInstance(this).loadRules();
//Rules.getInstance(this).add("18888888888", true);
//Rules.getInstance(this).add("16769764848", true);
startPhoneCallListener(); startPhoneCallListener();
MainServiceThread.getInstance(this, mMainServiceHandler).start(); MainServiceThread.getInstance(this, mMainServiceHandler).start();
@ -117,6 +158,14 @@ public class MainService extends Service {
} }
} }
public static boolean isPhoneInBoBullToon(String phone) {
if (_TomCat != null) {
return _TomCat.isPhoneBoBullToon(phone);
}
return false;
}
// 唤醒和绑定守护进程 // 唤醒和绑定守护进程
// //
void wakeupAndBindAssistant() { void wakeupAndBindAssistant() {
@ -137,7 +186,7 @@ public class MainService extends Service {
// LogUtils.d(TAG, "startService(intent)"); // LogUtils.d(TAG, "startService(intent)");
// bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT); // bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
} }
void startPhoneCallListener() { void startPhoneCallListener() {
Intent callListener = new Intent(this, CallListenerService.class); Intent callListener = new Intent(this, CallListenerService.class);
startService(callListener); startService(callListener);
@ -168,7 +217,7 @@ public class MainService extends Service {
// 停止主要进程 // 停止主要进程
MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true); MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true);
} }
super.onDestroy(); super.onDestroy();
@ -191,7 +240,11 @@ public class MainService extends Service {
if (mMainServiceBean.isEnable()) { if (mMainServiceBean.isEnable()) {
// 唤醒守护进程 // 唤醒守护进程
wakeupAndBindAssistant(); wakeupAndBindAssistant();
SOS.sosWinBollService(getApplicationContext(), new APPSOSBean(getPackageName(), MainService.class.getName())); if (App.isDebuging()) {
SOS.sosToAppBase(getApplicationContext(), MainService.class.getName());
} else {
SOS.sosToAppBaseBeta(getApplicationContext(), MainService.class.getName());
}
} }
isBound = false; isBound = false;
mAssistantService = null; mAssistantService = null;
@ -230,14 +283,40 @@ public class MainService extends Service {
public static void stopMainService(Context context) { public static void stopMainService(Context context) {
LogUtils.d(TAG, "stopMainService"); LogUtils.d(TAG, "stopMainService");
context.stopService(new Intent(context, MainService.class));
}
public static void startMainService(Context context) {
LogUtils.d(TAG, "startMainService");
context.startService(new Intent(context, MainService.class));
}
public static void restartMainService(Context context) {
LogUtils.d(TAG, "restartMainService");
MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
if (bean != null && bean.isEnable()) {
context.stopService(new Intent(context, MainService.class));
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
// }
context.startService(new Intent(context, MainService.class));
LogUtils.d(TAG, "已重启 MainService");
}
}
public static void stopMainServiceAndSaveStatus(Context context) {
LogUtils.d(TAG, "stopMainServiceAndSaveStatus");
MainServiceBean bean = new MainServiceBean(); MainServiceBean bean = new MainServiceBean();
bean.setIsEnable(false); bean.setIsEnable(false);
MainServiceBean.saveBean(context, bean); MainServiceBean.saveBean(context, bean);
context.stopService(new Intent(context, MainService.class)); context.stopService(new Intent(context, MainService.class));
} }
public static void startMainService(Context context) { public static void startMainServiceAndSaveStatus(Context context) {
LogUtils.d(TAG, "startMainService"); LogUtils.d(TAG, "startMainServiceAndSaveStatus");
MainServiceBean bean = new MainServiceBean(); MainServiceBean bean = new MainServiceBean();
bean.setIsEnable(true); bean.setIsEnable(true);
MainServiceBean.saveBean(context, bean); MainServiceBean.saveBean(context, bean);

View File

@ -6,11 +6,7 @@ package cc.winboll.studio.contacts.threads;
*/ */
import android.content.Context; import android.content.Context;
import cc.winboll.studio.contacts.handlers.MainServiceHandler; import cc.winboll.studio.contacts.handlers.MainServiceHandler;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.SOS;
import cc.winboll.studio.libappbase.bean.APPSOSBean;
import com.hjq.toast.ToastUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
public class MainServiceThread extends Thread { public class MainServiceThread extends Thread {

View File

@ -0,0 +1,123 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/06 21:08:16
* @Describe ContactUtils
*/
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import cc.winboll.studio.libappbase.LogUtils;
import java.util.HashMap;
import java.util.Map;
public class ContactUtils {
public static final String TAG = "ContactUtils";
Map<String, String> contactMap = new HashMap<>();
static volatile ContactUtils _ContactUtils;
Context mContext;
ContactUtils(Context context) {
mContext = context;
relaodContacts();
}
public synchronized static ContactUtils getInstance(Context context) {
if (_ContactUtils == null) {
_ContactUtils = new ContactUtils(context);
}
return _ContactUtils;
}
public void relaodContacts() {
readContacts();
}
private void readContacts() {
contactMap.clear();
ContentResolver contentResolver = mContext.getContentResolver();
Cursor cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//Map<String, String> contactMap = new HashMap<>();
contactMap.put(formatToSimplePhoneNumber(phoneNumber), displayName);
}
cursor.close();
}
// 此时 contactList 就是存储联系人信息的 Map 列表
}
public String getContactsName(String phone) {
String result = contactMap.get(formatToSimplePhoneNumber(phone));
return result == null ? "[NotInContacts]" : result;
}
// static String getSimplePhone(String phone) {
// return phone.replaceAll("[+\\s]", "");
// }
public static String formatToSimplePhoneNumber(String number) {
// 去除所有空格和非数字字符
return number.replaceAll("[^0-9]", "");
}
public static String getDisplayNameByPhone(Context context, String phoneNumber) {
String displayName = null;
ContentResolver resolver = context.getContentResolver();
String[] projection = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME};
Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, ContactsContract.CommonDataKinds.Phone.NUMBER + "=?", new String[]{phoneNumber}, null);
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
cursor.close();
}
return displayName;
}
public static String getDisplayNameByPhoneSimple(Context context, String phoneNumber) {
String displayName = null;
ContentResolver resolver = context.getContentResolver();
String[] projection = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME};
Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, ContactsContract.CommonDataKinds.Phone.NUMBER + "=?", new String[]{formatToSimplePhoneNumber(phoneNumber)}, null);
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
cursor.close();
}
return displayName;
}
public static boolean isPhoneInContacts(Context context, String phoneNumber) {
String szPhoneNumber = formatToSimplePhoneNumber(phoneNumber);
String szDisplayName = getDisplayNameByPhone(context, szPhoneNumber);
if (szDisplayName == null) {
LogUtils.d(TAG, String.format("Phone %s is not in contacts.", szPhoneNumber));
szPhoneNumber = formatToSpacePhoneNumber(szPhoneNumber);
szDisplayName = getDisplayNameByPhone(context, szPhoneNumber);
if (szDisplayName == null) {
LogUtils.d(TAG, String.format("Phone %s is not in contacts.", szPhoneNumber));
return false;
}
}
LogUtils.d(TAG, String.format("Phone %s is found in contacts %s.", szPhoneNumber, szDisplayName));
return true;
}
public static String formatToSpacePhoneNumber(String simpleNumber) {
// 去除所有空格和非数字字符
StringBuilder sbSpaceNumber = new StringBuilder();
String regex = "^1[0-9]{10}$";
if (simpleNumber.matches(regex)) {
sbSpaceNumber.append(simpleNumber.substring(0, 3));
sbSpaceNumber.append(" ");
sbSpaceNumber.append(simpleNumber.substring(3, 7));
sbSpaceNumber.append(" ");
sbSpaceNumber.append(simpleNumber.substring(7, 11));
}
return sbSpaceNumber.toString();
}
}

View File

@ -0,0 +1,24 @@
package cc.winboll.studio.contacts.utils;
import android.widget.EditText;
import cc.winboll.studio.libappbase.LogUtils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/04/13 00:59:13
* @Describe Int类型数字输入框工具集
*/
public class EditTextIntUtils {
public static final String TAG = "EditTextIntUtils";
public static int getIntFromEditText(EditText editText) {
try {
String sz = editText.getText().toString().trim();
return Integer.parseInt(sz);
} catch (NumberFormatException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
return 0;
}
}
}

View File

@ -0,0 +1,37 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/04/13 01:16:28
* @Describe Int数字操作工具集
*/
import cc.winboll.studio.libappbase.LogUtils;
public class IntUtils {
public static final String TAG = "IntUtils";
public static int getIntInRange(int origin, int range_a, int range_b) {
int min = Math.min(range_a, range_b);
int max = Math.max(range_a, range_b);
int res = Math.min(origin, max);
res = Math.max(res, min);
return res;
}
public static void unittest_getIntInRange() {
LogUtils.d(TAG, String.format("getIntInRange(-100, 5, 10); %d", getIntInRange(-100, 5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(8, 5, 10); %d", getIntInRange(8, 5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(200, 5, 10); %d", getIntInRange(200, 5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(-100, -5, 10); %d", getIntInRange(-100, -5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(9, -5, 10); %d", getIntInRange(9, -5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(100, -5, 10); %d", getIntInRange(100, -5, 10)));
LogUtils.d(TAG, String.format("getIntInRange(500, 5, -10); %d", getIntInRange(500, 5, -10)));
LogUtils.d(TAG, String.format("getIntInRange(4, 5, -10); %d", getIntInRange(4, 5, -10)));
LogUtils.d(TAG, String.format("getIntInRange(-20, 5, -10); %d", getIntInRange(-20, 5, -10)));
LogUtils.d(TAG, String.format("getIntInRange(500, 50, 10); %d", getIntInRange(500, 50, 10)));
LogUtils.d(TAG, String.format("getIntInRange(30, 50, 10); %d", getIntInRange(30, 50, 10)));
LogUtils.d(TAG, String.format("getIntInRange(6, 50, 10); %d", getIntInRange(6, 50, 10)));
}
}

View File

@ -0,0 +1,27 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/26 15:21:48
* @Describe PhoneUtils
*/
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
public class PhoneUtils {
public static final String TAG = "PhoneUtils";
public static void call(Context context, String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
context.startActivity(intent);
}
}

View File

@ -0,0 +1,32 @@
package cc.winboll.studio.contacts.utils;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/12/09 19:00:21
* @Describe .* 前置预防针
regex pointer preventive injection
简称 RegexPPi
*/
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexPPiUtils {
public static final String TAG = "RegexPPiUtils";
//
// 检验文本是否满足适合正则表达式模式计算
//
public static boolean isPPiOK(String text) {
//String text = "这里是一些任意的文本内容";
String regex = ".*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
/*if (matcher.matches()) {
System.out.println("文本满足该正则表达式模式");
} else {
System.out.println("文本不满足该正则表达式模式");
}*/
return matcher.matches();
}
}

View File

@ -0,0 +1,68 @@
package cc.winboll.studio.contacts.views;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/02 21:11:03
* @Describe 云盾防御信息
*/
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
import cc.winboll.studio.contacts.beans.SettingsModel;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.libappbase.LogUtils;
public class DuInfoTextView extends TextView {
public static final String TAG = "DuInfoTextView";
public static final int MSG_NOTIFY_INFO_UPDATE = 0;
Context mContext;
public DuInfoTextView(android.content.Context context) {
super(context);
}
public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public DuInfoTextView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
void initView(android.content.Context context) {
mContext = context;
updateInfo();
}
void updateInfo() {
LogUtils.d(TAG, "updateInfo()");
SettingsModel settingsModel = Rules.getInstance(mContext).getSettingsModel();
String info = String.format("(云盾防御值【%d/%d】)", settingsModel.getDunCurrentCount(), settingsModel.getDunTotalCount());
setText(info);
}
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == MSG_NOTIFY_INFO_UPDATE) {
updateInfo();
}
}
};
public void notifyInfoUpdate() {
LogUtils.d(TAG, "notifyInfoUpdate()");
mHandler.sendMessage(mHandler.obtainMessage(MSG_NOTIFY_INFO_UPDATE));
}
}

View File

@ -0,0 +1,220 @@
package cc.winboll.studio.contacts.views;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/04 10:51:50
* @Describe CustomHorizontalScrollView
*/
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.libappbase.LogUtils;
public class LeftScrollView extends HorizontalScrollView {
public static final String TAG = "LeftScrollView";
private LinearLayout contentLayout;
private LinearLayout toolLayout;
private TextView textView;
private Button editButton;
private Button deleteButton;
private Button upButton;
private Button downButton;
private float mStartX;
private float mEndX;
private boolean isScrolling = false;
private int nScrollAcceptSize;
public LeftScrollView(Context context) {
super(context);
init();
}
public LeftScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LeftScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void addContentLayout(TextView textView) {
contentLayout.addView(textView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
}
public void setContentWidth(int contentWidth) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) contentLayout.getLayoutParams();
layoutParams.width = contentWidth;
contentLayout.setLayoutParams(layoutParams);
}
private void init() {
View viewMain = inflate(getContext(), R.layout.view_left_scroll, null);
// 创建内容布局
contentLayout = viewMain.findViewById(R.id.content_layout);
toolLayout = viewMain.findViewById(R.id.action_layout);
//LogUtils.d(TAG, String.format("getWidth() %d", getWidth()));
addView(viewMain);
// 创建编辑按钮
editButton = viewMain.findViewById(R.id.edit_btn);
// 创建删除按钮
deleteButton = viewMain.findViewById(R.id.delete_btn);
// 向上按钮
upButton = viewMain.findViewById(R.id.up_btn);
// 向下按钮
downButton = viewMain.findViewById(R.id.down_btn);
// 编辑按钮点击事件
editButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onEdit();
}
}
});
// 删除按钮点击事件
deleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onDelete();
}
}
});
// 编辑按钮点击事件
upButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onUp();
}
}
});
// 删除按钮点击事件
downButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onActionListener != null) {
onActionListener.onDown();
}
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
LogUtils.d(TAG, "ACTION_DOWN");
mStartX = event.getX();
// isScrolling = false;
break;
case MotionEvent.ACTION_MOVE:
//LogUtils.d(TAG, "ACTION_MOVE");
// float currentX = event.getX();
// float deltaX = mStartX - currentX;
// //mLastX = currentX;
// if (Math.abs(deltaX) > 0) {
// isScrolling = true;
// }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (getScrollX() > 0) {
LogUtils.d(TAG, "ACTION_UP");
mEndX = event.getX();
LogUtils.d(TAG, String.format("mStartX %f, mEndX %f", mStartX, mEndX));
if (mEndX < mStartX) {
LogUtils.d(TAG, String.format("mEndX >= mStartX \ngetScrollX() %d", getScrollX()));
//if (getScrollX() > editButton.getWidth()) {
if (Math.abs(mStartX - mEndX) > editButton.getWidth()) {
smoothScrollToRight();
} else {
smoothScrollToLeft();
}
} else {
LogUtils.d(TAG, String.format("mEndX >= mStartX \ngetScrollX() %d", getScrollX()));
//if (getScrollX() > deleteButton.getWidth()) {
if (Math.abs(mEndX - mStartX) > deleteButton.getWidth()) {
smoothScrollToLeft();
} else {
smoothScrollToRight();
}
}
}
break;
}
return super.onTouchEvent(event);
}
void smoothScrollToRight() {
mEndX = 0;
mStartX = 0;
View childView = getChildAt(0);
if (childView != null) {
// 计算需要滑动到最右边的距离
int scrollToX = childView.getWidth() - getWidth();
// 确保滑动距离不小于0
final int scrollToX2 = Math.max(0, scrollToX);
// 平滑滑动到最右边
post(new Runnable() {
@Override
public void run() {
smoothScrollTo(scrollToX2, 0);
LogUtils.d(TAG, "smoothScrollTo(0, 0);");
}
});
LogUtils.d(TAG, "smoothScrollTo(scrollToX, 0);");
}
}
void smoothScrollToLeft() {
mEndX = 0;
mStartX = 0;
// 在手指抬起时使用 post 方法调用 smoothScrollTo(0, 0)
post(new Runnable() {
@Override
public void run() {
smoothScrollTo(0, 0);
LogUtils.d(TAG, "smoothScrollTo(0, 0);");
}
});
}
// 设置文本内容
public void setText(CharSequence text) {
textView.setText(text);
}
// 定义回调接口
public interface OnActionListener {
void onEdit();
void onDelete();
void onUp();
void onDown();
}
private OnActionListener onActionListener;
public void setOnActionListener(OnActionListener listener) {
this.onActionListener = listener;
}
}

View File

@ -0,0 +1,14 @@
package cc.winboll.studio.contacts.views;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/19 14:04:20
* @Describe 云盾滑视度热备控件
*/
public class ScrollDoView {
public static final String TAG = "ScrollDoView";
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#ff000000"
android:pathData="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0,0 1,21 16.5V20A1,1 0,0 1,20 21A17,17 0,0 1,3 4A1,1 0,0 1,4 3H7.5A1,1 0,0 1,8.5 4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#000000" /> <!-- 设置边框宽度和颜色,这里是黑色 1dp 边框 -->
<solid android:color="#ffffff" /> <!-- 设置背景颜色,这里是白色 -->
</shape>

View File

@ -6,10 +6,16 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<TextView <androidx.appcompat.widget.Toolbar
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Text" android:id="@+id/toolbar"/>
android:id="@+id/page_text"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:id="@+id/aboutviewroot_ll"/>
</LinearLayout> </LinearLayout>

View File

@ -12,16 +12,26 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/activitymainToolbar1"/> android:id="@+id/activitymainToolbar1"/>
<androidx.viewpager.widget.ViewPager <LinearLayout
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1.0" android:padding="10dp"
android:id="@+id/activitymainViewPager1"/> android:layout_weight="1.0">
<androidx.viewpager.widget.ViewPager
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:id="@+id/viewPager"/>
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:id="@+id/tabLayout"/>
</LinearLayout>
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:id="@+id/activitymainTabLayout1"/>
</LinearLayout> </LinearLayout>

View File

@ -1,95 +1,98 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context=".phonecallui.PhoneCallActivity"> tools:context=".phonecallui.PhoneCallActivity">
<RelativeLayout <RelativeLayout
android:id="@+id/rl_user_info" android:id="@+id/rl_user_info"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="300dp" android:layout_height="300dp"
android:background="@color/colorPrimaryDark"> android:background="@color/colorPrimaryDark"
android:layout_marginTop="100dp">
<TextView <TextView
android:id="@+id/tv_call_number_label" android:id="@+id/tv_call_number_label"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_above="@+id/tv_call_number" android:layout_above="@+id/tv_call_number"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:gravity="center" android:gravity="center"
android:text="来电号码" android:text="来电号码"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="18sp" /> android:textSize="18sp"/>
<TextView
android:id="@+id/tv_call_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="28sp"
android:textStyle="bold"
android:layout_centerInParent="true"
tools:text="133-9527-9527"/>
<TextView </RelativeLayout>
android:id="@+id/tv_call_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="28sp"
android:textStyle="bold"
android:layout_centerInParent="true"
tools:text="133-9527-9527" />
</RelativeLayout> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<RelativeLayout <TextView
android:layout_width="match_parent" android:id="@+id/tv_phone_calling_time"
android:layout_height="match_parent" android:layout_width="wrap_content"
android:background="@android:color/white"> android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="24dp"
android:text="通话中01:33"
android:textColor="@android:color/black"
android:textSize="18sp"
android:visibility="gone"
tools:visibility="visible"/>
<TextView <RelativeLayout
android:id="@+id/tv_phone_calling_time" android:layout_width="wrap_content"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_centerInParent="true">
android:layout_centerHorizontal="true"
android:layout_marginTop="24dp"
android:text="通话中01:33"
android:textColor="@android:color/black"
android:textSize="18sp"
android:visibility="gone"
tools:visibility="visible" />
<RelativeLayout <TextView
android:layout_width="wrap_content" android:id="@+id/tv_phone_hang_up"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_centerInParent="true"> android:layout_height="wrap_content"
android:drawablePadding="16dp"
android:drawableTop="@drawable/ic_phone_hang_up"
android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:padding="8dp"
android:text="挂 断"
android:textColor="@android:color/black"
tools:visibility="visible"/>
<TextView <TextView
android:id="@+id/tv_phone_hang_up" android:id="@+id/tv_phone_pick_up"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawablePadding="16dp" android:layout_marginLeft="50dp"
android:drawableTop="@drawable/ic_phone_hang_up" android:layout_toRightOf="@id/tv_phone_hang_up"
android:foreground="?android:attr/selectableItemBackground" android:drawablePadding="16dp"
android:gravity="center" android:drawableTop="@drawable/ic_phone_pick_up"
android:padding="8dp" android:foreground="?android:attr/selectableItemBackground"
android:text="挂 断" android:gravity="center"
android:textColor="@android:color/black" android:padding="8dp"
tools:visibility="visible" /> android:text="接 听"
android:textColor="@android:color/black"
android:visibility="gone"
tools:visibility="visible"/>
<TextView </RelativeLayout>
android:id="@+id/tv_phone_pick_up"
android:layout_width="wrap_content" </RelativeLayout>
android:layout_height="wrap_content"
android:layout_marginLeft="50dp" </LinearLayout>
android:layout_toRightOf="@id/tv_phone_hang_up"
android:drawablePadding="16dp"
android:drawableTop="@drawable/ic_phone_pick_up"
android:foreground="?android:attr/selectableItemBackground"
android:gravity="center"
android:padding="8dp"
android:text="接 听"
android:textColor="@android:color/black"
android:visibility="gone"
tools:visibility="visible" />
</RelativeLayout>
</RelativeLayout>
</LinearLayout>

View File

@ -1,51 +1,309 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activitymainToolbar1"/>
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content">
android:layout_weight="1.0">
<TextView <androidx.appcompat.widget.Toolbar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="应用权限设置:"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="right"> android:id="@+id/activitymainToolbar1"/>
<Button <LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0">
<TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="悬浮窗设置" android:text="服务设置:"/>
android:id="@+id/activitysettingsButton2"
android:onClick="onCanDrawOverlays"/>
<Button <LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Switch
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="主要服务"
android:id="@+id/sw_mainservice"
android:layout_margin="5dp"/>
</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="云盾设置:"/>
<cc.winboll.studio.contacts.views.DuInfoTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_DunInfo"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Switch
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="是否启用云盾防御"
android:layout_margin="5dp"
android:id="@+id/sw_IsEnableDun"
android:onClick="onSW_IsEnableDun"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="初始防御层的叠加数量:"/>
<EditText
android:layout_width="0dp"
android:inputType="number"
android:layout_height="wrap_content"
android:ems="10"
android:layout_weight="1.0"
android:id="@+id/et_DunTotalCount"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="防御层每次恢复的时间间隔(秒钟)"/>
<EditText
android:layout_width="0dp"
android:inputType="number"
android:layout_height="wrap_content"
android:ems="10"
android:layout_weight="1.0"
android:id="@+id/et_DunResumeSecondCount"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="防御层每次恢复的叠加数量:"/>
<EditText
android:layout_width="0dp"
android:inputType="number"
android:layout_height="wrap_content"
android:ems="10"
android:layout_weight="1.0"
android:id="@+id/et_DunResumeCount"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="默认拨号设置" android:text="应用权限设置:"/>
android:id="@+id/activitysettingsButton1"
android:onClick="onDefaultPhone"/> <LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="悬浮窗设置"
android:id="@+id/activitysettingsButton2"
android:onClick="onCanDrawOverlays"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="默认拨号设置"
android:id="@+id/activitysettingsButton1"
android:onClick="onDefaultPhone"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="音量设置:"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:id="@+id/tv_volume"/>
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:max="100"
android:id="@+id/bellvolume"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拨不通电话记录查询:"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:layout_margin="10dp">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载 BoBullToon"
android:onClick="onDownloadBoBullToon"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right|center_vertical"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查询电话:"/>
<EditText
android:layout_width="0dp"
android:inputType="phone"
android:layout_height="wrap_content"
android:ems="10"
android:layout_weight="1.0"
android:id="@+id/activitysettingsEditText1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查询记录"
android:onClick="onSearchBoBullToonPhone"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Phone Connect Rule :"
android:layout_weight="1.0"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加新规则"
android:onClick="onAddNewConnectionRule"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@drawable/recycler_view_border"
android:layout_margin="5dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="单元测试:"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Unit Test"
android:onClick="onUnitTest"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="其他:"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="关于应用"
android:onClick="onAbout"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </ScrollView>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test Main"
android:onClick="onTestMain"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试号码:"/>
<EditText
android:layout_width="0dp"
android:inputType="phone"
android:layout_height="wrap_content"
android:ems="10"
android:layout_weight="1.0"
android:id="@+id/phone_et"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test Phone"
android:onClick="onTestPhone"/>
</LinearLayout>
<cc.winboll.studio.libappbase.LogView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:id="@+id/logview"/>
</LinearLayout>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="0dp"
android:ems="10"
android:layout_height="wrap_content"
android:layout_weight="1.0"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
android:id="@+id/page_text"/>
</LinearLayout>
</LinearLayout>

View File

@ -6,8 +6,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.tencent.tencentmap.mapsdk.maps.TextureMapView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/mapView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />

View File

@ -1,15 +1,36 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/search_edit_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Search contacts"
android:padding="16dp"
android:layout_weight="1.0"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dial"
android:id="@+id/btn_dial"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/contacts_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
android:id="@+id/page_text"/>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/call_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/call_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<View
android:layout_width="20dp"
android:layout_height="20dp"
android:background="#FFE7E7E7"
android:id="@+id/itemcalllogView1"
android:layout_alignParentLeft="true"/>
<cc.winboll.studio.libaes.views.AOHPCTCSeekBar
android:layout_toRightOf="@id/itemcalllogView1"
android:layout_toLeftOf="@id/itemcalllogView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/aohpctcseekbar_dial"/>
<View
android:layout_width="20dp"
android:layout_height="20dp"
android:background="#FFE7E7E7"
android:id="@+id/itemcalllogView2"
android:layout_alignParentRight="true"/>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,67 @@
<?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="wrap_content"
android:orientation="vertical">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/contact_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/contact_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="☎"
android:textStyle="bold"/>
<TextView
android:id="@+id/contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<View
android:layout_width="20dp"
android:layout_height="20dp"
android:background="#FFE7E7E7"
android:id="@+id/itemcalllogView1"
android:layout_alignParentLeft="true"/>
<cc.winboll.studio.libaes.views.AOHPCTCSeekBar
android:layout_toRightOf="@id/itemcalllogView1"
android:layout_toLeftOf="@id/itemcalllogView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/aohpctcseekbar_dial"/>
<View
android:layout_width="20dp"
android:layout_height="20dp"
android:background="#FFE7E7E7"
android:id="@+id/itemcalllogView2"
android:layout_alignParentRight="true"/>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,52 @@
<?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="wrap_content"
android:orientation="horizontal">
<!-- 内容区域 -->
<LinearLayout
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/white">
<!-- 这里放置你的列表项内容 -->
</LinearLayout>
<!-- 操作按钮 -->
<LinearLayout
android:id="@+id/action_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@color/lightgray">
<Button
android:id="@+id/edit_btn"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:text="编辑"
android:background="@color/blue" />
<Button
android:id="@+id/up_btn"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="△"
android:background="@color/green" />
<Button
android:id="@+id/down_btn"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="▽"
android:background="@color/green" />
<Button
android:id="@+id/delete_btn"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:text="删除"
android:background="@color/red" />
</LinearLayout>
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More