Compare commits

..

53 Commits

Author SHA1 Message Date
ZhanGSKen
35c639e7c9 <contacts>APK 1.0.20 release Publish. 2025-03-19 08:51:20 +08:00
ZhanGSKen
05cc5c72f8 添加联系人查询,在联系人名单内就允许连接。 2025-03-19 08:48:03 +08:00
ZhanGSKen
3eaec5ed73 Merge branch 'appbase' into contacts 2025-03-17 09:54:20 +08:00
ZhanGSKen
1dad84b65e <libappbase>Library Release 15.0.3 2025-03-17 09:51:28 +08:00
ZhanGSKen
1ae377e665 <appbase>APK 15.0.3 release Publish. 2025-03-17 09:51:04 +08:00
ZhanGSKen
02fa0a4134 优化调试窗口 2025-03-17 09:40:03 +08:00
ZhanGSKen
a730aa3f92 优化日志UI 2025-03-17 09:37:12 +08:00
ZhanGSKen
486a17d4b2 <contacts>APK 1.0.19 release Publish. 2025-03-09 20:33:54 +08:00
ZhanGSKen
6cde3be042 更新类库 2025-03-09 20:31:51 +08:00
ZhanGSKen
445bf47e38 <contacts>APK 1.0.18 release Publish. 2025-03-09 20:06:59 +08:00
ZhanGSKen
322e1426d7 修复拉动拨号控件在列表滚动时图标位置不稳定的BUG. 2025-03-09 20:05:52 +08:00
ZhanGSKen
9ff752b652 <contacts>APK 1.0.17 release Publish. 2025-03-09 17:42:52 +08:00
ZhanGSKen
95cba7166e UI优化 2025-03-09 17:41:19 +08:00
ZhanGSKen
ba4354ad59 <contacts>APK 1.0.16 release Publish. 2025-03-09 14:02:13 +08:00
ZhanGSKen
69fd92f4a0 Merge branch 'appbase' into contacts 2025-03-09 14:01:10 +08:00
ZhanGSKen
73608ed6e6 更新拉动拨号控件 2025-03-09 14:00:36 +08:00
ZhanGSKen
5746ef4569 Merge branch 'contacts' into appbase 2025-03-09 11:43:12 +08:00
ZhanGSKen
ad745a9cc1 更新类库 2025-03-09 11:42:53 +08:00
ZhanGSKen
e3571ae39f <contacts>APK 1.0.15 release Publish. 2025-03-08 04:50:59 +08:00
ZhanGSKen
261c87a42d 修复应用日志时间显示BUG 2025-03-08 04:49:35 +08:00
ZhanGSKen
0db31c07ba <contacts>APK 1.0.14 release Publish. 2025-03-08 04:37:47 +08:00
ZhanGSKen
87dea88e36 <contacts>APK 1.0.13 release Publish. 2025-03-08 04:37:15 +08:00
ZhanGSKen
5779f195f8 设置UI为拖动拨号方式 2025-03-08 04:36:07 +08:00
ZhanGSKen
02f2d4f0bc <contacts>APK 1.0.12 release Publish. 2025-03-06 21:44:44 +08:00
ZhanGSKen
f6bece28ac 添加通信记录联系人显示 2025-03-06 21:43:50 +08:00
ZhanGSKen
559f1c58ba <contacts>APK 1.0.11 release Publish. 2025-03-06 20:00:52 +08:00
ZhanGSKen
4bb814308b 添加单号码单元测试 2025-03-06 19:59:11 +08:00
ZhanGSKen
05a9fc5275 <contacts>APK 1.0.10 release Publish. 2025-03-05 20:51:08 +08:00
ZhanGSKen
74d5239898 添加规则删除确认框。 2025-03-05 20:49:39 +08:00
ZhanGSKen
948141d5a9 规则编辑列表的项滑动逻辑完成。 2025-03-05 20:41:29 +08:00
ZhanGSKen
3535df8b3e <contacts>APK 1.0.9 release Publish. 2025-03-05 17:21:57 +08:00
ZhanGSKen
bd4ba7b291 连接规则记录操作接口完成 2025-03-05 17:20:00 +08:00
ZhanGSKen
9ff8b1ed80 1629 2025-03-05 16:29:55 +08:00
ZhanGSKen
859238a173 1852 2025-03-04 18:52:16 +08:00
ZhanGSKen
53eced104e <contacts>APK 1.0.8 release Publish. 2025-03-04 01:25:27 +08:00
ZhanGSKen
b45119b487 添加调试信息 2025-03-04 01:24:16 +08:00
ZhanGSKen
fdddde33b5 <contacts>APK 1.0.7 release Publish. 2025-03-03 20:51:18 +08:00
ZhanGSKen
f263733609 UI界面优化 2025-03-03 20:42:00 +08:00
ZhanGSKen
51a1cf1e26 重构为Java7语法 2025-03-03 20:23:00 +08:00
ZhanGSKen
093772c824 <contacts>APK 1.0.6 release Publish. 2025-03-03 17:39:16 +08:00
ZhanGSKen
bf3e9bdc91 云盾防御体系成型 2025-03-03 17:37:42 +08:00
ZhanGSKen
0f72817b50 <contacts>APK 1.0.5 release Publish. 2025-03-02 18:48:57 +08:00
ZhanGSKen
c827b2cbad 完成电话连接规则的编辑和保存 2025-03-02 18:26:45 +08:00
ZhanGSKen
02673d19dd <contacts>APK 1.0.4 release Publish. 2025-02-26 19:10:28 +08:00
ZhanGSKen
38884d3457 基本拨号功能完成 2025-02-26 19:07:55 +08:00
ZhanGSKen
ecb56df773 1821 2025-02-26 18:22:10 +08:00
ZhanGSKen
a15f6bad8f 1509 2025-02-26 15:09:29 +08:00
ZhanGSKen
2f0293103c 添加通话记录和联系人 2025-02-26 14:10:16 +08:00
ZhanGSKen
d4135f0104 <contacts>APK 1.0.3 release Publish. 2025-02-26 05:10:50 +08:00
ZhanGSKen
bdb9bc7637 性能优化 2025-02-26 05:08:56 +08:00
ZhanGSKen
e7633e53ed <contacts>APK 1.0.2 release Publish. 2025-02-26 04:37:18 +08:00
ZhanGSKen
3734a659ff 添加主要服务启动设置 2025-02-26 04:35:14 +08:00
ZhanGSKen
b9f740c386 添加系统设置静音时的权限异常处理,添加固定的应用铃声音量设置。忽略其他应用设置的铃声音量。 2025-02-26 03:40:41 +08:00
122 changed files with 6258 additions and 2011 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Sat Mar 15 15:30:02 HKT 2025 #Mon Mar 17 09:51:20 HKT 2025
stageCount=3 stageCount=4
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.0 baseVersion=15.0
publishVersion=15.0.2 publishVersion=15.0.3
buildCount=0 buildCount=0
baseBetaVersion=15.0.3 baseBetaVersion=15.0.4

View File

@@ -179,16 +179,16 @@
</ScrollView> </ScrollView>
<cc.winboll.studio.libappbase.LogView
android:layout_height="500dp"
android:layout_width="match_parent"
android:id="@+id/activitymainLogView1"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<cc.winboll.studio.libappbase.LogView
android:layout_height="300dp"
android:layout_width="match_parent"
android:id="@+id/activitymainLogView1"/>
</LinearLayout> </LinearLayout>

View File

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

View File

@@ -18,18 +18,18 @@ def genVersionName(def versionName){
} }
android { android {
compileSdkVersion 30 compileSdkVersion 32
buildToolsVersion "30.0.3" buildToolsVersion "33.0.3"
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.apputils" applicationId "cc.winboll.studio.apputils"
minSdkVersion 26 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 30
versionCode 1 versionCode 1
// versionName 更新后需要手动设置 // versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0 // 项目模块目录的 build.gradle 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.0" versionName "9.2"
if(true) { if(true) {
versionName = genVersionName("${versionName}") versionName = genVersionName("${versionName}")
} }
@@ -41,26 +41,15 @@ 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 project(':libapputils') api project(':libapputils')
api 'cc.winboll.studio:libappbase:1.0.3'
api fileTree(dir: 'libs', include: ['*.jar']) api fileTree(dir: 'libs', include: ['*.jar'])
implementation 'cc.winboll.studio:libappbase:15.0.5'
// 二维码类库
implementation 'com.google.zxing:core:3.4.1'
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
// 网络连接类库
//implementation 'com.squareup.okhttp3:okhttp:4.4.1'
// Html 解析
//implementation 'org.jsoup:jsoup:1.13.1'
// SSH
//implementation 'com.jcraft:jsch:0.1.55'
// 应用介绍页类库
//implementation 'io.github.medyo:android-about-page:2.0.0'
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Mar 18 10:22:27 HKT 2025 #Sat Jan 18 13:03:10 HKT 2025
stageCount=3 stageCount=2
libraryProject=libapputils libraryProject=libapputils
baseVersion=15.0 baseVersion=9.2
publishVersion=15.0.2 publishVersion=9.2.1
buildCount=0 buildCount=0
baseBetaVersion=15.0.3 baseBetaVersion=9.2.2

View File

@@ -8,7 +8,7 @@
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/ic_winboll" android:icon="@drawable/ic_winboll"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/MyUtilsTheme" android:theme="@style/WinBoll.SupportThemeNoActionBar"
android:supportsRtl="true"> android:supportsRtl="true">
<activity <activity
@@ -27,8 +27,10 @@
</activity> </activity>
<activity android:name=".TestWinBollActivity"/>
<activity android:name=".TestStringToQrCodeViewActivity"/> <activity android:name=".TestStringToQrCodeViewActivity"/>
</application> </application>
</manifest> </manifest>

View File

@@ -5,83 +5,27 @@ package cc.winboll.studio.apputils;
* @Date 2024/12/08 15:10:51 * @Date 2024/12/08 15:10:51
* @Describe 全局应用类 * @Describe 全局应用类
*/ */
import android.app.Application; import android.view.Gravity;
import android.content.Context; import cc.winboll.studio.libapputils.app.WinBollApplication;
import android.widget.Toast; import com.hjq.toast.ToastUtils;
import cc.winboll.studio.libappbase.GlobalApplication; import com.hjq.toast.style.WhiteToastStyle;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libapputils.app.MyActivityLifecycleCallbacks;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.DebugBean;
public class App extends GlobalApplication { public class App extends WinBollApplication {
public static final String TAG = "App"; public static final String TAG = "App";
public static final String _ACTION_DEBUGVIEW = App.class.getName() + "_ACTION_DEBUGVIEW"; public static final String _ACTION_DEBUGVIEW = WinBollApplication.class.getName() + "_ACTION_DEBUGVIEW";
//static volatile WinBollApplication _WinBollApplication = null;
MyActivityLifecycleCallbacks mMyActivityLifecycleCallbacks;
// 标记当前应用是否处于调试状态
static volatile boolean isDebug = false;
public synchronized static void setIsDebug(boolean isDebug) {
App.isDebug = isDebug;
}
public static boolean isDebug() {
return isDebug;
}
MyActivityLifecycleCallbacks getMyActivityLifecycleCallbacks() {
return mMyActivityLifecycleCallbacks;
}
@Override
public Context getApplicationContext() {
return super.getApplicationContext();
}
@Override
public Application getApplication() {
return this;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// 应用环境初始化, 基本调试环境 // 初始化 Toast 框架
// //
// 初始化日志模块 ToastUtils.init(this);
//LogUtils.init(this); // 设置 Toast 布局样式
//ToastUtils.setView(R.layout.view_toast);
try { ToastUtils.setStyle(new WhiteToastStyle());
// 初始化 Toast 框架 ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
// ToastUtils.init(this);
// // 设置 Toast 布局样式
// //ToastUtils.setView(R.layout.view_toast);
// ToastUtils.setStyle(new WhiteToastStyle());
// ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
// 设置应用调试标志
DebugBean debugBean = DebugBean.loadBean(this, DebugBean.class);
if (debugBean == null) {
//ToastUtils.show("debugBean == null");
setIsDebug(false);
} else {
//ToastUtils.show("saveDebugStatus(" + String.valueOf(debugBean.isDebuging()) + ")");
setIsDebug(debugBean.isDebuging());
}
// 应用窗口管理模块参数设置
//
mMyActivityLifecycleCallbacks = new MyActivityLifecycleCallbacks();
registerActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks);
// 设置默认 WinBoll 应用 UI 类型
WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Service);
//ToastUtils.show("WinBollUI_TYPE " + getWinBollUI_TYPE());
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
} }
} }

View File

@@ -1,83 +1,28 @@
package cc.winboll.studio.apputils; package cc.winboll.studio.apputils;
import cc.winboll.studio.apputils.R;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
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.widget.Toast; import android.widget.Toast;
import android.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libappbase.utils.ToastUtils;
import cc.winboll.studio.libapputils.activities.AssetsHtmlActivity; import cc.winboll.studio.libapputils.activities.AssetsHtmlActivity;
import cc.winboll.studio.libapputils.activities.LogActivity;
import cc.winboll.studio.libapputils.activities.QRCodeDecodeActivity; import cc.winboll.studio.libapputils.activities.QRCodeDecodeActivity;
import cc.winboll.studio.libapputils.app.AboutActivityFactory; import cc.winboll.studio.libapputils.app.WinBollActivity;
import cc.winboll.studio.libapputils.app.IWinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager; import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.APPInfo; import cc.winboll.studio.libapputils.log.LogActivity;
import cc.winboll.studio.libapputils.view.AboutView; import cc.winboll.studio.libapputils.log.LogUtils;
import cc.winboll.studio.libapputils.view.YesNoAlertDialog; import com.hjq.toast.ToastUtils;
import java.util.List;
import java.util.Set;
final public class MainActivity extends Activity implements IWinBollActivity { final public class MainActivity extends WinBollActivity {
public static final String TAG = "MainActivity"; public static final String TAG = "MainActivity";
public static final int REQUEST_QRCODEDECODE_ACTIVITY = 0; public static final int REQUEST_QRCODEDECODE_ACTIVITY = 0;
Toolbar mToolbar;
LogView mLogView;
@Override @Override
public Activity getActivity() { protected boolean isEnableDisplayHomeAsUp() {
return this;
}
@Override
public APPInfo getAppInfo() {
String szBranchName = "apputils";
APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
appInfo.setAppName("APPUtils");
appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
appInfo.setAppDescription("APPUtils Description");
appInfo.setAppGitName("APP");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(szBranchName);
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=APP");
appInfo.setAppAPKName("APPUtils");
appInfo.setAppAPKFolderName("APPUtils");
return appInfo;
//return null;
}
@Override
public String getTag() {
return TAG;
}
@Override
public boolean isAddWinBollToolBar() {
return true;
}
@Override
public Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean isEnableDisplayHomeAsUp() {
return false; return false;
} }
@@ -86,22 +31,8 @@ final public class MainActivity extends Activity implements IWinBollActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
mLogView = findViewById(R.id.logview); //Toolbar toolbar = findViewById(R.id.activitymainToolbar1);
mLogView.start(); //setActionBar(toolbar);
// 初始化工具栏
mToolbar = findViewById(R.id.activitymainToolbar1);
setActionBar(mToolbar);
if (isEnableDisplayHomeAsUp()) {
// 显示后退按钮
getActionBar().setDisplayHomeAsUpEnabled(true);
}
getActionBar().setSubtitle(getTag());
checkResolveActivity();
archiveInstance();
// 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回 // 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
//if (prosessIntents(getIntent())) return; //if (prosessIntents(getIntent())) return;
@@ -113,94 +44,44 @@ final public class MainActivity extends Activity implements IWinBollActivity {
// LogUtils.d(TAG, "BuildConfig.DEBUG : " + Boolean.toString(BuildConfig.DEBUG)); // LogUtils.d(TAG, "BuildConfig.DEBUG : " + Boolean.toString(BuildConfig.DEBUG));
} }
boolean checkResolveActivity() {
PackageManager packageManager = getPackageManager();
//Intent intent = new Intent("your_action_here");
Intent intent = getIntent();
if (intent != null) {
List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfoList.size() > 0) {
// 传入的Intent action在Activity清单的intent-filter的action节点里有定义
if (intent.getAction() != null) {
if (intent.getAction().equals(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW)) {
App.setIsDebug(true);
//ToastUtils.show!("WinBollApplication.setIsDebug(true) by action : " + intent.getAction());
}
}
return true;
} else {
// 传入的Intent action在Activity清单的intent-filter的action节点里没有定义
//ToastUtils.show("false : " + intent.getAction());
return false;
}
}
// action在清单文件中没有声明
ToastUtils.show("false");
return false;
}
void archiveInstance() {
Intent intent = getIntent();
StringBuilder sb = new StringBuilder("\n### Archive Instance ###\n");
if (intent != null) {
ComponentName componentName = intent.getComponent();
if (componentName != null) {
String packageName = componentName.getPackageName();
//Log.d("AppStarter", "启动本应用的应用包名: " + packageName);
sb.append("启动本应用的应用包名: \n" + packageName);
}
sb.append("\nImplicit Intent Tracker \n接收到的 Intent 动作: \n" + intent.getAction());
Set<String> categories = intent.getCategories();
if (categories != null) {
for (String category : categories) {
sb.append("\n接收到的 Intent 类别 :\n" + category);
}
}
Uri data = intent.getData();
if (data != null) {
sb.append("\n接收到的 Intent 数据 :\n" + data.toString());
}
} else {
sb.append("Intent is null.");
}
sb.append("\n\n");
LogUtils.d(TAG, sb.toString());
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// 缓存当前 activity
WinBollActivityManager.getInstance(this).add(this);
}
@Override
public void onDestroy() {
WinBollActivityManager.getInstance(this).registeRemove(this);
super.onDestroy();
}
public void onTestLogClick(View view) { public void onTestLogClick(View view) {
LogUtils.d(TAG, "onTestLogClick"); LogUtils.d(TAG, "onTestLogClick");
Toast.makeText(getApplication(), "onTestLogClick", Toast.LENGTH_SHORT).show(); Toast.makeText(getApplication(), "onTestLogClick", Toast.LENGTH_SHORT).show();
} }
public void onLogUtilsClick(View view) { public void onLogUtilsClick(View view) {
// Intent intent = new Intent(this, LogActivity.class); Intent intent = new Intent(this, LogActivity.class);
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
// intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
// startActivity(intent); startActivity(intent);
//WinBollActivityManager.getInstance().printAvtivityListInfo();
WinBollActivityManager.getInstance(this).startWinBollActivity(this, LogActivity.class);
} }
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// setSubTitle("");
}
@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);
// }
// //
// 处理传入的 Intent 数据 // 处理传入的 Intent 数据
// //
@@ -245,87 +126,39 @@ final public class MainActivity extends Activity implements IWinBollActivity {
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public String getTag() {
//ToastUtils.show("onCreateOptionsMenu"); return TAG;
getMenuInflater().inflate(R.menu.toolbar_main, menu); }
if (isAddWinBollToolBar()) {
//ToastUtils.show("mIWinBoll.isAddWinBollToolBar()");
getMenuInflater().inflate(R.menu.toolbar_winboll_shared_main, menu);
}
if (App.isDebug()) {
getMenuInflater().inflate(R.menu.toolbar_studio_debug, menu);
}
@Override
protected boolean isAddWinBollToolBar() {
return true;
}
@Override
protected Toolbar initToolBar() {
return findViewById(R.id.activitymainToolbar1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.item_exit) { if (item.getItemId() == R.id.item_testwinboll) {
exit(); WinBollActivityManager.getInstance(this).startWinBollActivity(this, TestWinBollActivity.class);
return true;
} else if (item.getItemId() == R.id.item_about) {
AboutActivityFactory.showAboutActivity(this, getAppInfo());
return true;
} else if (item.getItemId() == R.id.item_teststringtoqrcodeview) { } else if (item.getItemId() == R.id.item_teststringtoqrcodeview) {
WinBollActivityManager.getInstance(this).startWinBollActivity(this, TestStringToQrCodeViewActivity.class); WinBollActivityManager.getInstance(this).startWinBollActivity(this, TestStringToQrCodeViewActivity.class);
} else if (item.getItemId() == R.id.item_testqrcodedecodeactivity) { } else if (item.getItemId() == R.id.item_testqrcodedecodeactivity) {
Intent intent = new Intent(this, QRCodeDecodeActivity.class); Intent intent = new Intent(this, QRCodeDecodeActivity.class);
startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY); startActivityForResult(intent, REQUEST_QRCODEDECODE_ACTIVITY);
} else if (item.getItemId() == R.id.item_testcrashreport) {
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
getString(i);
}
return true;
} else if (item.getItemId() == R.id.item_log) {
WinBollActivityManager.getInstance(this).startWinBollActivity(this, LogActivity.class);
return true;
} else if (item.getItemId() == R.id.item_exitdebug) {
//AboutView.setApp2NormalMode(this);
return true;
} else if (item.getItemId() == android.R.id.home) {
WinBollActivityManager.getInstance(this).finish(this);
return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
void about() {
// Intent intent = new Intent(this, AboutActivity.class);
// intent.putExtra(AboutActivity.EXTRA_APPINFO, AboutActivityFactory.buildAPPBranchInfo(this));
// WinBollActivityManager.getInstance(this).startWinBollActivity(this, intent, AboutActivity.class);
}
void exit() {
YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onYes() {
WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
}
@Override
public void onNo() {
}
};
YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
}
@Override
public void onBackPressed() {
if (WinBollActivityManager.getInstance(getApplicationContext()).isFirstIWinBollActivity(this)) {
exit();
} else {
WinBollActivityManager.getInstance(this).finish(this);
super.onBackPressed();
}
}
public void onTestAboutActivity(View view) {
about();
}
public void onTestJavascriptHtmlActivity(View view) { public void onTestJavascriptHtmlActivity(View view) {
Intent intent = new Intent(this, AssetsHtmlActivity.class); Intent intent = new Intent(this, AssetsHtmlActivity.class);
intent.putExtra(AssetsHtmlActivity.EXTRA_HTMLFILENAME, "javascript_test.html"); intent.putExtra(AssetsHtmlActivity.EXTRA_HTMLFILENAME, "javascript_test.html");
@@ -333,25 +166,17 @@ final public class MainActivity extends Activity implements IWinBollActivity {
} }
@Override @Override
protected void onResume() { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onResume(); switch (requestCode) {
mLogView.start(); case REQUEST_QRCODEDECODE_ACTIVITY : {
String text = data.getStringExtra(QRCodeDecodeActivity.EXTRA_RESULT);
ToastUtils.show(text);
break;
}
default : {
ToastUtils.show(String.format("%d, %d", requestCode, resultCode));
super.onActivityResult(requestCode, resultCode, data);
}
}
} }
/*@Override
protected void onActivithyResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_QRCODEDECODE_ACTIVITY : {
if (data != null) {
String text = data.getStringExtra(QRCodeDecodeActivity.EXTRA_RESULT);
ToastUtils.show(text);
}
break;
}
default : {
//ToastUtils.show(String.format("%d, %d", requestCode, resultCode));
super.prosessActivityResult(requestCode, resultCode, data);
}
}
}*/
} }

View File

@@ -1,50 +1,37 @@
package cc.winboll.studio.apputils; package cc.winboll.studio.apputils;
import android.os.Bundle;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libapputils.app.WinBollActivity;
import cc.winboll.studio.libapputils.view.StringToQrCodeView;
/** /**
* @Author ZhanGSKen@QQ.COM * @Author ZhanGSKen@QQ.COM
* @Date 2025/01/17 19:50:46 * @Date 2025/01/17 19:50:46
*/ */
import cc.winboll.studio.apputils.R; public class TestStringToQrCodeViewActivity extends WinBollActivity {
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toolbar;
import cc.winboll.studio.libapputils.app.IWinBollActivity;
import cc.winboll.studio.libapputils.bean.APPInfo;
import cc.winboll.studio.libapputils.view.StringToQrCodeView;
public class TestStringToQrCodeViewActivity extends Activity implements IWinBollActivity {
public static final String TAG = "TestStringToQrCodeViewActivity"; public static final String TAG = "TestStringToQrCodeViewActivity";
StringToQrCodeView mStringToQrCodeView; StringToQrCodeView mStringToQrCodeView;
@Override
public Activity getActivity() {
return this;
}
@Override
public APPInfo getAppInfo() {
return null;
}
@Override @Override
public String getTag() { public String getTag() {
return TAG; return TAG;
} }
@Override @Override
public Toolbar initToolBar() { protected Toolbar initToolBar() {
return findViewById(R.id.activityteststringtoqrcodeviewToolbar1); return findViewById(R.id.activityteststringtoqrcodeviewToolbar1);
} }
@Override @Override
public boolean isEnableDisplayHomeAsUp() { protected boolean isEnableDisplayHomeAsUp() {
return true; return true;
} }
@Override @Override
public boolean isAddWinBollToolBar() { protected boolean isAddWinBollToolBar() {
return true; return true;
} }
@@ -54,4 +41,10 @@ public class TestStringToQrCodeViewActivity extends Activity implements IWinBoll
setContentView(R.layout.activity_teststringtoqrcodeview); setContentView(R.layout.activity_teststringtoqrcodeview);
mStringToQrCodeView = findViewById(R.id.activityteststringtoqrcodeviewStringToQrCodeView1); mStringToQrCodeView = findViewById(R.id.activityteststringtoqrcodeviewStringToQrCodeView1);
} }
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setSubTitle(TAG);
}
} }

View File

@@ -0,0 +1,52 @@
package cc.winboll.studio.apputils;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libapputils.activities.AssetsHtmlActivity;
import cc.winboll.studio.libapputils.app.WinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2025/01/13 15:09:46
*/
public class TestWinBollActivity extends WinBollActivity {
public static final String TAG = "TestWinBollActivity";
@Override
public String getTag() {
return TAG;
}
@Override
protected Toolbar initToolBar() {
return findViewById(R.id.activitytestwinbollToolbar1);
}
@Override
protected boolean isEnableDisplayHomeAsUp() {
return true;
}
@Override
protected boolean isAddWinBollToolBar() {
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testwinboll);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setSubTitle(TAG);
}
}

View File

@@ -5,7 +5,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.widget.Toolbar <androidx.appcompat.widget.Toolbar
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/activitymainToolbar1"/>
@@ -27,7 +27,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="TestLog" android:text="TestLog"
android:textAllCaps="false" android:textAllCaps="false"
android:onClick="onTestLogClick"/> android:onClick="onTestLogClick"/>
<Button <Button
@@ -39,27 +39,20 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:orientation="horizontal" 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:gravity="right">
<Button <Button
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAllCaps="false" android:textAllCaps="false"
android:text="Test Javascript Html Activity" android:text="Test Javascript Html Activity"
android:onClick="onTestJavascriptHtmlActivity"/> android:onClick="onTestJavascriptHtmlActivity"/>
</LinearLayout> </LinearLayout>
<cc.winboll.studio.libappbase.LogView
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="Button"
android:id="@+id/logview"
android:layout_weight="1.0"/>
</LinearLayout> </LinearLayout>

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/activityteststringtoqrcodeviewToolbar1"/> android:id="@+id/activityteststringtoqrcodeviewToolbar1"/>

View File

@@ -7,7 +7,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#FF36B5B3"> android:background="#FF36B5B3">
<android.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/activitytestwinbollToolbar1"/> android:id="@+id/activitytestwinbollToolbar1"/>

View File

@@ -0,0 +1,32 @@
<?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

@@ -3,14 +3,14 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/item_testwinboll" android:id="@+id/item_testwinboll"
android:title="TestWinBollActivity"/> android:title="TestWinBollActivity"
app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/item_teststringtoqrcodeview" android:id="@+id/item_teststringtoqrcodeview"
android:title="TestStringToQrCodeViewActivity"/> android:title="TestStringToQrCodeViewActivity"
app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/item_testqrcodedecodeactivity" android:id="@+id/item_testqrcodedecodeactivity"
android:title="TestQRCodeDecodeActivity"/> android:title="TestQRCodeDecodeActivity"
<item app:showAsAction="ifRoom"/>
android:id="@+id/item_testcrashreport"
android:title="TestCrashReportActivity"/>
</menu> </menu>

View File

@@ -4,4 +4,7 @@
<color name="colorPrimary">#FF196ABC</color> <color name="colorPrimary">#FF196ABC</color>
<color name="colorPrimaryDark">#FF002B57</color> <color name="colorPrimaryDark">#FF002B57</color>
<color name="colorAccent">#FF80BFFF</color> <color name="colorAccent">#FF80BFFF</color>
<color name="colorToastFrame">#FFA9A9A9</color>
<color name="colorToastShadow">#FF000000</color>
<color name="colorToastBackgroung">#FFFFFFFF</color>
</resources> </resources>

View File

@@ -1,14 +1,3 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="MyUtilsTheme" parent="APPBaseTheme">
<item name="attrColorPrimary">@color/colorPrimary</item>
<item name="themeGlobalCrashActivity">@style/MyUtilsGlobalCrashActivityTheme</item>
</style>
<style name="MyUtilsGlobalCrashActivityTheme" parent="GlobalCrashActivityTheme">
<item name="colorTittle">#FFFFFFFF</item>
<item name="colorTittleBackgound">#FF00A4B3</item>
<item name="colorText">#FFFFFFFF</item>
<item name="colorTextBackgound">#FF000000</item>
</style>
</resources> </resources>

View File

@@ -18,13 +18,13 @@ def genVersionName(def versionName){
} }
android { android {
compileSdkVersion 32 compileSdkVersion 30
buildToolsVersion "33.0.3" buildToolsVersion "30.0.3"
defaultConfig { defaultConfig {
applicationId "cc.winboll.studio.contacts" applicationId "cc.winboll.studio.contacts"
minSdkVersion 21 minSdkVersion 26
targetSdkVersion 30 targetSdkVersion 29
versionCode 1 versionCode 1
// versionName 更新后需要手动设置 // versionName 更新后需要手动设置
// 项目模块目录的 build.gradle 文件的 stageCount=0 // 项目模块目录的 build.gradle 文件的 stageCount=0
@@ -41,31 +41,41 @@ 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 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0'
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.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 'cc.winboll.studio:libapputils:9.3.2'
api 'cc.winboll.studio:libappbase:1.5.6'
api fileTree(dir: 'libs', include: ['*.jar']) api fileTree(dir: 'libs', include: ['*.jar'])
// 权限请求框架https://github.com/getActivity/XXPermissions
implementation 'com.github.getActivity:XXPermissions:18.63'
// 下拉控件
implementation 'com.baoyz.pullrefreshlayout:library:1.2.0'
// 拼音搜索
// https://mvnrepository.com/artifact/com.github.open-android/pinyin4j
implementation 'com.github.open-android:pinyin4j:2.5.0'
// SSH
implementation 'com.jcraft:jsch:0.1.55'
// Html 解析
implementation 'org.jsoup:jsoup:1.13.1'
// 二维码类库
implementation 'com.google.zxing:core:3.4.1'
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
// 应用介绍页类库
implementation 'io.github.medyo:android-about-page:2.0.0'
// 吐司类库
implementation 'com.github.getActivity:ToastUtils:10.5'
// 网络连接类库
implementation 'com.squareup.okhttp3:okhttp:4.4.1'
// Android 类库
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'
implementation 'cc.winboll.studio:libappbase:2.1.5'
implementation 'cc.winboll.studio:libapputils:9.4.4'
implementation 'cc.winboll.studio:libaes:7.6.12'
} }

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 #Wed Mar 19 08:51:20 HKT 2025
stageCount=2 stageCount=21
libraryProject= libraryProject=
baseVersion=1.0 baseVersion=1.0
publishVersion=1.0.1 publishVersion=1.0.20
buildCount=0 buildCount=0
baseBetaVersion=1.0.2 baseBetaVersion=1.0.21

View File

@@ -29,9 +29,10 @@
<!-- 更改您的音频设置 --> <!-- 更改您的音频设置 -->
<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"/>
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
@@ -65,7 +66,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 +183,8 @@
</provider> </provider>
<activity android:name="cc.winboll.studio.contacts.activities.UnitTestActivity"/>
</application> </application>
</manifest> </manifest>

View File

@@ -8,6 +8,7 @@ package cc.winboll.studio.contacts;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libapputils.app.WinBollActivityManager; import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import com.hjq.toast.ToastUtils;
public class App extends GlobalApplication { public class App extends GlobalApplication {
@@ -23,6 +24,8 @@ public class App extends GlobalApplication {
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");
ToastUtils.init(this);
} }
} }

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.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.libapputils.app.IWinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.APPInfo; 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,6 +64,9 @@ 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;
private static final int DIALER_REQUEST_CODE = 1; private static final int DIALER_REQUEST_CODE = 1;
@Override @Override
@@ -102,6 +99,7 @@ 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);
// 初始化工具栏 // 初始化工具栏
@@ -112,17 +110,39 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
getSupportActionBar().setSubtitle(getTag()); getSupportActionBar().setSubtitle(getTag());
tabLayout = findViewById(R.id.tabLayout);
viewPager = findViewById(R.id.viewPager);
initData(); // 创建Fragment列表和标题列表
initView(); List<Fragment> fragmentList = new ArrayList<>();
//initPoint();//调用初始化导航原点的方法 List<String> tabTitleList = new ArrayList<>();
viewPager.addOnPageChangeListener(this);//滑动事件 fragmentList.add(CallLogFragment.newInstance(0));
fragmentList.add(ContactsFragment.newInstance(1));
fragmentList.add(LogFragment.newInstance(2));
tabTitleList.add("通话记录");
tabTitleList.add("联系人");
tabTitleList.add("应用日志");
ViewPager viewPager = findViewById(R.id.activitymainViewPager1); // 设置ViewPager的适配器
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager()); MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
viewPager.setAdapter(pagerAdapter); viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1);
// 关联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) {
@@ -140,36 +160,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 +301,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();
@@ -302,25 +389,25 @@ final public class MainActivity extends AppCompatActivity implements IWinBollAct
return false; return false;
} }
@Override // @Override
public void onBackPressed() { // public void onBackPressed() {
exit(); // exit();
} // }
//
void exit() { // void exit() {
YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){ // YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
//
@Override // @Override
public void onYes() { // public void onYes() {
WinBollActivityManager.getInstance(getApplicationContext()).finishAll(); // WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
} // }
//
@Override // @Override
public void onNo() { // public void onNo() {
} // }
}; // };
YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener); // 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 +418,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

@@ -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,32 @@ 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.libappbase.LogUtils;
import cc.winboll.studio.libapputils.app.IWinBollActivity;
import cc.winboll.studio.libapputils.bean.APPInfo;
import com.hjq.toast.ToastUtils; import com.hjq.toast.ToastUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List;
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity { public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
@@ -33,6 +47,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;
// 云盾防御层数量
EditText etDunTotalCount;
// 防御层恢复时间间隔(秒钟)
EditText etDunResumeSecondCount;
// 每次恢复防御层数
EditText etDunResumeCount;
// 是否启用云盾
Switch swIsEnableDun;
private RecyclerView recyclerView;
private PhoneConnectRuleAdapter adapter;
private List<PhoneConnectRuleModel> ruleList;
@Override @Override
public APPInfo getAppInfo() { public APPInfo getAppInfo() {
@@ -77,6 +110,133 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
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 +254,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("允许显示悬浮框")

View File

@@ -0,0 +1,91 @@
package cc.winboll.studio.contacts.activities;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import cc.winboll.studio.contacts.R;
import cc.winboll.studio.contacts.dun.Rules;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import android.widget.EditText;
/**
* @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) {
Rules rules = Rules.getInstance(this);
// 如果没有规则就添加测试规则
if (rules.getPhoneBlacRuleBeanList().size() == 0) {
// 手机号码允许
// 中国手机号码正则表达式以1开头第二位可以是3、4、5、6、7、8、9后面跟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,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,250 @@
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 java.util.ArrayList;
import java.util.List;
import android.widget.LinearLayout;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
import cc.winboll.studio.contacts.views.LeftScrollView;
import com.hjq.toast.ToastUtils;
import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
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,135 @@
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;
public class SettingsModel extends BaseBean {
public static final String TAG = "SettingsModel";
// 云盾防御层数量
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 = dunTotalCount;
this.dunCurrentCount = dunCurrentCount;
this.dunResumeSecondCount = dunResumeSecondCount;
this.dunResumeCount = dunResumeCount;
this.isEnableDun = isEnableDun;
}
public void setDunTotalCount(int dunTotalCount) {
this.dunTotalCount = dunTotalCount;
}
public int getDunTotalCount() {
return dunTotalCount;
}
public void setDunCurrentCount(int dunCurrentCount) {
this.dunCurrentCount = dunCurrentCount;
}
public int getDunCurrentCount() {
return dunCurrentCount;
}
public void setDunResumeSecondCount(int dunResumeSecondCount) {
this.dunResumeSecondCount = dunResumeSecondCount;
}
public int getDunResumeSecondCount() {
return dunResumeSecondCount;
}
public void setDunResumeCount(int dunResumeCount) {
this.dunResumeCount = dunResumeCount;
}
public int getDunResumeCount() {
return dunResumeCount;
}
public void setIsEnableDun(boolean isEnableDun) {
this.isEnableDun = isEnableDun;
}
public boolean isEnableDun() {
return isEnableDun;
}
@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(jsonReader.nextInt());
} else if (name.equals("dunCurrentCount")) {
setDunCurrentCount(jsonReader.nextInt());
} else if (name.equals("dunResumeSecondCount")) {
setDunResumeSecondCount(jsonReader.nextInt());
} else if (name.equals("dunResumeCount")) {
setDunResumeCount(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 = "https://gitea.winboll.cc//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,35 @@ 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.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 +41,167 @@ 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();
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, mSettingsModel.getDunResumeSecondCount() * 1000);
}
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开头第二位可以是3、4、5、6、7、8、9后面跟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,125 @@
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.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 java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CallLogFragment extends Fragment {
public static final String TAG = "CallFragment";
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<>();
public static CallLogFragment newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
CallLogFragment fragment = new CallLogFragment();
fragment.setArguments(args);
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 {
readCallLog();
}
}
@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) {
readCallLog();
}
}
}
private void readCallLog() {
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";
}
}
}

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

@@ -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

@@ -57,10 +57,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 +73,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 +93,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 +104,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 +120,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 +142,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 +157,6 @@ public class PhoneCallActivity extends AppCompatActivity implements View.OnClick
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
phoneCallManager.destroy(); phoneCallManager.destroy();
} }
} }

View File

@@ -21,8 +21,6 @@ import cc.winboll.studio.libappbase.LogUtils;
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;
private final Call.Callback callback = new Call.Callback() { private final Call.Callback callback = new Call.Callback() {
@Override @Override
@@ -61,25 +59,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,12 +119,8 @@ 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);
} }
public enum CallType { public enum CallType {

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 ? "[NoInContacts]" : 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,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,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

@@ -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,290 @@
<?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>
</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

@@ -0,0 +1,15 @@
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

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>

View File

@@ -0,0 +1,44 @@
<?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"
android:padding="16dp">
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入规则文本"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right|center_vertical">
<CheckBox
android:id="@+id/checkbox_allow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="允许连接"/>
<CheckBox
android:id="@+id/checkbox_enable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启用"/>
<Button
android:id="@+id/button_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确定"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,16 @@
<?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"
android:padding="2dp">
<cc.winboll.studio.contacts.views.LeftScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/scrollView"/>
</LinearLayout>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:id="@+id/scrollView">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- 内容区域 -->
<LinearLayout
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/white">
<!-- 这里放置你的列表项内容 -->
<TextView
android:id="@+id/text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="16sp"/>
</LinearLayout>
<!-- 操作按钮 -->
<LinearLayout
android:id="@+id/action_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="@color/lightgray">
<Button
android:id="@+id/edit_btn"
android:layout_width="80dp"
android:layout_height="match_parent"
android:text="编辑"
android:background="@color/blue" />
<Button
android:id="@+id/delete_btn"
android:layout_width="80dp"
android:layout_height="match_parent"
android:text="删除"
android:background="@color/red" />
</LinearLayout>
</LinearLayout>
</HorizontalScrollView>

View File

@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu 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">
<item
android:id="@+id/item_call"
android:title="CallActivity"/>
<item <item
android:id="@+id/item_settings" android:id="@+id/item_settings"
android:title="SettingsActivity"/> android:title="Settings"
app:showAsAction="ifRoom"/>
</menu> </menu>

View File

@@ -4,4 +4,10 @@
<color name="colorPrimary">#FF196ABC</color> <color name="colorPrimary">#FF196ABC</color>
<color name="colorPrimaryDark">#FF002B57</color> <color name="colorPrimaryDark">#FF002B57</color>
<color name="colorAccent">#FF80BFFF</color> <color name="colorAccent">#FF80BFFF</color>
<color name="blue">#FF379AFF</color>
<color name="green">#FF69E551</color>
<color name="red">#FFE55151</color>
<color name="white">#FFFFFFFF</color>
<color name="lightgray">#FFE0E0E0</color>
</resources> </resources>

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Sat Mar 15 15:29:07 HKT 2025 #Mon Mar 17 09:51:04 HKT 2025
stageCount=3 stageCount=4
libraryProject=libappbase libraryProject=libappbase
baseVersion=15.0 baseVersion=15.0
publishVersion=15.0.2 publishVersion=15.0.3
buildCount=0 buildCount=0
baseBetaVersion=15.0.3 baseBetaVersion=15.0.4

View File

@@ -190,6 +190,7 @@ public class LogView extends RelativeLayout {
// 加载标签表 // 加载标签表
mListViewTags = findViewById(cc.winboll.studio.libappbase.R.id.tags_listview); mListViewTags = findViewById(cc.winboll.studio.libappbase.R.id.tags_listview);
mListViewTags.setVerticalOffset(10);
mTAGListAdapter = new TAGListAdapter(mContext, mapTAGList); mTAGListAdapter = new TAGListAdapter(mContext, mapTAGList);
mListViewTags.setAdapter(mTAGListAdapter); mListViewTags.setAdapter(mTAGListAdapter);

View File

@@ -12,7 +12,9 @@ import android.widget.ListView;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
public class HorizontalListView extends ListView { public class HorizontalListView extends ListView {
public static final String TAG = "HorizontalListView"; public static final String TAG = "HorizontalListView";
int verticalOffset = 0;
public HorizontalListView(Context context) { public HorizontalListView(Context context) {
super(context); super(context);
@@ -25,25 +27,10 @@ public class HorizontalListView extends ListView {
public HorizontalListView(Context context, AttributeSet attrs, int defStyle) { public HorizontalListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
} }
// @Override
// protected void onLayout(boolean changed, int l, int t, int r, int b) { public void setVerticalOffset(int verticalOffset) {
// super.onLayout(changed, l, t, r, b); this.verticalOffset = verticalOffset;
// int childCount = getChildCount(); }
// int left = getPaddingLeft();
// // 获取HorizontalListView的高度
// int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
// for (int i = 0; i < childCount; i++) {
// View child = getChildAt(i);
// int width = child.getMeasuredWidth();
// int height = child.getMeasuredHeight();
// // 计算子项在垂直方向上的居中偏移量
// int verticalOffset = (viewHeight - height) / 2;
// // 设置子视图的位置,实现水平布局并垂直居中
// child.layout(left, getPaddingTop() + verticalOffset, left + width, getPaddingTop() + verticalOffset + height);
// left += width;
// }
// }
//
@Override @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) { protected void onLayout(boolean changed, int l, int t, int r, int b) {
@@ -52,7 +39,7 @@ public class HorizontalListView extends ListView {
int left = getPaddingLeft(); int left = getPaddingLeft();
int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
LogUtils.d(TAG, String.format("HorizontalListView的高度 %d", viewHeight)); LogUtils.d(TAG, String.format("HorizontalListView的高度 %d", viewHeight));
int verticalOffset = 20;
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
View child = getChildAt(i); View child = getChildAt(i);
// 计算每个子视图的宽度和高度 // 计算每个子视图的宽度和高度
@@ -60,7 +47,7 @@ public class HorizontalListView extends ListView {
int height = child.getMeasuredHeight(); int height = child.getMeasuredHeight();
//LogUtils.d(TAG, String.format("child : width %d , height %d", width, height)); //LogUtils.d(TAG, String.format("child : width %d , height %d", width, height));
// 设置子视图的位置,实现水平布局 // 设置子视图的位置,实现水平布局
child.layout(left, verticalOffset, left + width, verticalOffset + height); child.layout(left, verticalOffset, left + width, verticalOffset + height);
left += width; left += width;
} }
@@ -68,15 +55,11 @@ public class HorizontalListView extends ListView {
@Override @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
//super.onMeasure(widthMeasureSpec, newHeightMeasureSpec); //super.onMeasure(widthMeasureSpec, newHeightMeasureSpec);
int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
//LogUtils.d(TAG, String.format("newWidthMeasureSpec %d, newHeightMeasureSpec %d", newWidthMeasureSpec, newHeightMeasureSpec)); //LogUtils.d(TAG, String.format("newWidthMeasureSpec %d, newHeightMeasureSpec %d", newWidthMeasureSpec, newHeightMeasureSpec));
super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
// ViewGroup.LayoutParams params = getLayoutParams();
// params.width = getMeasuredWidth();
} }
} }

View File

@@ -0,0 +1,13 @@
<?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" /> <!-- 这里可调整边框宽度和颜色 -->
<solid android:color="@android:color/transparent" />
<corners
android:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip"
android:topLeftRadius="6dip"
android:topRightRadius="6dip" />
</shape>

View File

@@ -10,8 +10,8 @@
<shape android:shape="rectangle" > <shape android:shape="rectangle" >
<gradient <gradient
android:angle="270" android:angle="270"
android:endColor="#0F000000" android:endColor="@color/colorAccent"
android:startColor="#0F000000" /> android:startColor="@color/colorAccent" />
<corners <corners
android:bottomLeftRadius="6dip" android:bottomLeftRadius="6dip"
android:bottomRightRadius="6dip" android:bottomRightRadius="6dip"

View File

@@ -5,18 +5,18 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#FFEEEEEE"> android:background="@drawable/bg_border_round">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="24dp" android:layout_height="@dimen/button_height"
android:layout_marginLeft="5dp" android:layout_marginLeft="5dp"
android:id="@+id/viewlogtagTextView1"/> android:id="@+id/viewlogtagTextView1"/>
<CheckBox <CheckBox
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="24dp" android:layout_height="@dimen/button_height"
android:id="@+id/viewlogtagCheckBox1"/> android:id="@+id/viewlogtagCheckBox1"/>
</LinearLayout> </LinearLayout>

View File

@@ -72,27 +72,29 @@
android:layout_below="@+id/viewlogRelativeLayoutToolbar" android:layout_below="@+id/viewlogRelativeLayoutToolbar"
android:id="@+id/viewlogLinearLayout1" android:id="@+id/viewlogLinearLayout1"
android:gravity="center_vertical" android:gravity="center_vertical"
android:background="#FFC0F4C2"> android:background="@drawable/bg_shadow">
<CheckBox <CheckBox
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="@dimen/button_height"
android:text="ALL" android:text="ALL"
android:id="@+id/viewlogCheckBox1"/> android:id="@+id/viewlogCheckBox1"
android:background="@drawable/bg_border_round"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"/>
<HorizontalScrollView <HorizontalScrollView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/bg_border"
android:scrollbars="none" android:scrollbars="none"
android:layout_weight="1.0" android:padding="5dp"
android:background="#FF97F2E3"> android:layout_weight="1.0">
<cc.winboll.studio.libappbase.views.HorizontalListView <cc.winboll.studio.libappbase.views.HorizontalListView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/view_border" android:id="@+id/tags_listview"/>
android:id="@+id/tags_listview"
android:paddingLeft="10dp"/>
</HorizontalScrollView> </HorizontalScrollView>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 定义一个名为text_size_normal的尺寸值为16sp -->
<dimen name="text_size_normal">16sp</dimen>
<!-- 定义一个名为margin_small的尺寸值为8dp -->
<dimen name="margin_small">8dp</dimen>
<!-- 定义一个名为image_width的尺寸值为200dp -->
<dimen name="button_height">24dp</dimen>
</resources>

View File

@@ -4,12 +4,12 @@ apply from: '../.winboll/winboll_lib_build.gradle'
apply from: '../.winboll/winboll_lint_build.gradle' apply from: '../.winboll/winboll_lint_build.gradle'
android { android {
compileSdkVersion 30 compileSdkVersion 32
buildToolsVersion "30.0.3" buildToolsVersion "33.0.3"
defaultConfig { defaultConfig {
minSdkVersion 26 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 30
} }
buildTypes { buildTypes {
release { release {
@@ -17,22 +17,28 @@ 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 'com.google.zxing:core:3.4.1'
api 'com.journeyapps:zxing-android-embedded:3.6.0'
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:libappbase:1.0.3'
api fileTree(dir: 'libs', include: ['*.jar']) api fileTree(dir: 'libs', include: ['*.jar'])
implementation 'cc.winboll.studio:libappbase:15.0.5'
// 二维码类库
implementation 'com.google.zxing:core:3.4.1'
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
// 网络连接类库
//implementation 'com.squareup.okhttp3:okhttp:4.4.1'
// Html 解析
//implementation 'org.jsoup:jsoup:1.13.1'
// SSH
//implementation 'com.jcraft:jsch:0.1.55'
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Mar 18 10:22:27 HKT 2025 #Sat Jan 18 13:02:39 HKT 2025
stageCount=3 stageCount=2
libraryProject=libapputils libraryProject=libapputils
baseVersion=15.0 baseVersion=9.2
publishVersion=15.0.2 publishVersion=9.2.1
buildCount=0 buildCount=0
baseBetaVersion=15.0.3 baseBetaVersion=9.2.2

View File

@@ -18,9 +18,6 @@
<!-- 拍摄照片和视频 --> <!-- 拍摄照片和视频 -->
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
<!-- 拥有完全的网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<application android:networkSecurityConfig="@xml/network_security_config"> <application android:networkSecurityConfig="@xml/network_security_config">
<activity <activity
@@ -29,7 +26,7 @@
android:launchMode="standard"/> android:launchMode="standard"/>
<activity <activity
android:name=".activities.LogActivity" android:name=".log.LogActivity"
android:label="LogActivity" android:label="LogActivity"
android:launchMode="standard"/> android:launchMode="standard"/>
@@ -48,4 +45,4 @@
</application> </application>
</manifest> </manifest>

View File

@@ -1,79 +1,70 @@
package cc.winboll.studio.libapputils.activities; package cc.winboll.studio.libapputils.activities;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/07/14 13:20:33
* @Describe 应用介绍页
*/
import cc.winboll.studio.libapputils.R;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.LinearLayout; import androidx.appcompat.widget.Toolbar;
import android.widget.Toolbar; import cc.winboll.studio.libapputils.R;
import cc.winboll.studio.libapputils.app.IWinBollActivity; import cc.winboll.studio.libapputils.app.WinBollActivity;
import cc.winboll.studio.libapputils.app.WinBollActivityManager; import cc.winboll.studio.libapputils.app.WinBollActivityManager;
import cc.winboll.studio.libapputils.bean.APPInfo; import cc.winboll.studio.libapputils.log.LogUtils;
import cc.winboll.studio.libapputils.view.AboutView;
final public class AboutActivity extends Activity implements IWinBollActivity { /**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/07/14 13:20:33
* @Describe AboutFragment Test
*/
final public class AboutActivity extends WinBollActivity {
public static final String TAG = "AboutActivity"; public static final String TAG = "AboutActivity";
public static final String EXTRA_APPINFO = "EXTRA_APPINFO";
APPInfo mAPPInfo;
@Override
public Activity getActivity() {
return this;
}
@Override
public APPInfo getAppInfo() {
return null;
}
@Override @Override
public String getTag() { public String getTag() {
return TAG; return TAG;
} }
@Override @Override
public boolean isEnableDisplayHomeAsUp() { protected boolean isEnableDisplayHomeAsUp() {
return true; return true;
} }
@Override
public boolean isAddWinBollToolBar() {
return false;
}
@Override
public Toolbar initToolBar() {
return findViewById(R.id.activityaboutToolbar1);
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about); setContentView(R.layout.activity_about);
Intent intent = getIntent();
if (intent != null) {
mAPPInfo = (APPInfo)intent.getSerializableExtra(EXTRA_APPINFO);
}
if (mAPPInfo == null) {
mAPPInfo = new APPInfo();
}
AboutView aboutView = new AboutView(this, mAPPInfo); /*AboutView aboutView = findViewById(R.id.activityaboutAboutView1);
LinearLayout llMain = findViewById(R.id.activityaboutLinearLayout1); aboutView.setOnRequestDevUserInfoAutofillListener(new AboutView.OnRequestDevUserInfoAutofillListener(){
llMain.addView(aboutView);
//ToastUtils.show(TAG); @Override
public void requestAutofill(EditText etDevUserName, EditText etDevUserPassword) {
AutofillManager autofillManager = (AutofillManager) getSystemService(AutofillManager.class);
if (autofillManager!= null) {
//ToastUtils.show("0");
autofillManager.requestAutofill(findViewById(R.id.usernameEditText));
autofillManager.requestAutofill(findViewById(R.id.passwordEditText));
}
}
});*/
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setSubTitle(TAG);
}
@Override
protected boolean isAddWinBollToolBar() {
return false;
}
@Override
protected Toolbar initToolBar() {
return findViewById(R.id.activityaboutToolbar1);
} }
@Override @Override
@@ -82,11 +73,15 @@ final public class AboutActivity extends Activity implements IWinBollActivity {
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.item_help) { if (item.getItemId() == R.id.item_help) {
WinBollActivityManager.getInstance(this).startWinBollActivity(this, AssetsHtmlActivity.class); WinBollActivityManager.getInstance(this).startWinBollActivity(this, AssetsHtmlActivity.class);
} }
// else if (item.getItemId() == android.R.id.home) {
// WinBollActivityManager.getInstance(this).finish(this);
// }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }

View File

@@ -5,28 +5,28 @@ package cc.winboll.studio.libapputils.activities;
* @Date 2025/01/03 11:02:49 * @Date 2025/01/03 11:02:49
* @Describe 一个可以浏览随 APP 附带的 Html 文档的窗口 * @Describe 一个可以浏览随 APP 附带的 Html 文档的窗口
*/ */
import cc.winboll.studio.libapputils.R;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libapputils.R;
import cc.winboll.studio.libapputils.app.IWinBollActivity; import cc.winboll.studio.libapputils.app.WinBollActivity;
import cc.winboll.studio.libapputils.bean.APPInfo; import cc.winboll.studio.libapputils.log.LogUtils;
import cc.winboll.studio.libapputils.view.SimpleWebView; import cc.winboll.studio.libapputils.view.SimpleWebView;
import com.hjq.toast.ToastUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import android.os.PersistableBundle;
public class AssetsHtmlActivity extends Activity implements IWinBollActivity { public class AssetsHtmlActivity extends WinBollActivity {
public static final String TAG = "AssetsHtmlActivity"; public static final String TAG = "AssetsHtmlActivity";
public static final String EXTRA_HTMLFILENAME = "EXTRA_HTMLFILENAME"; public static final String EXTRA_HTMLFILENAME = "EXTRA_HTMLFILENAME";
String mszHelpIndexFilePath = ""; String mszHelpIndexFilePath = "";
Uri mszHelpIndexFileUri; Uri mszHelpIndexFileUri;
Context mContext; Context mContext;
@@ -34,34 +34,24 @@ public class AssetsHtmlActivity extends Activity implements IWinBollActivity {
// Assets 文件夹里的 Html 文件的名称 // Assets 文件夹里的 Html 文件的名称
String mszHtmlFileName; String mszHtmlFileName;
@Override
public Activity getActivity() {
return this;
}
@Override
public APPInfo getAppInfo() {
return null;
}
@Override @Override
public String getTag() { public String getTag() {
return TAG; return TAG;
} }
@Override @Override
public boolean isEnableDisplayHomeAsUp() { protected boolean isEnableDisplayHomeAsUp() {
return true; return true;
} }
@Override @Override
public boolean isAddWinBollToolBar() { protected boolean isAddWinBollToolBar() {
return false; return false;
} }
@Override @Override
public Toolbar initToolBar() { protected Toolbar initToolBar() {
return findViewById(R.id.activityassetshtmlToolbar1); return findViewById(R.id.activityassetshtmlToolbar1);
} }
@@ -91,7 +81,7 @@ public class AssetsHtmlActivity extends Activity implements IWinBollActivity {
if (szTemp != null && !szTemp.trim().equals("")) { if (szTemp != null && !szTemp.trim().equals("")) {
mszHtmlFileName = szTemp.trim(); mszHtmlFileName = szTemp.trim();
} }
//ToastUtils.show(mszHtmlFileName); ToastUtils.show(mszHtmlFileName);
} }
// 与其他应用分享 html 帮助 // 与其他应用分享 html 帮助
@@ -117,6 +107,13 @@ public class AssetsHtmlActivity extends Activity implements IWinBollActivity {
// myWebView.loadUrl(mszHelpIndexFileUri.toString()); // myWebView.loadUrl(mszHelpIndexFileUri.toString());
} }
@Override
public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onPostCreate(savedInstanceState, persistentState);
setSubTitle(mszHtmlFileName);
//setSubTitle(TAG);
}
// //
void initWebViewFromAssets(String szHtmlFileName) { void initWebViewFromAssets(String szHtmlFileName) {
try { try {

View File

@@ -1,68 +0,0 @@
package cc.winboll.studio.libapputils.activities;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/08 00:19:39
* @Describe LogActivity
*/
import cc.winboll.studio.libapputils.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toolbar;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libapputils.app.IWinBollActivity;
import cc.winboll.studio.libapputils.bean.APPInfo;
public class LogActivity extends Activity implements IWinBollActivity {
public static final String TAG = "LogActivity";
LogView mLogView;
@Override
public Activity getActivity() {
return this;
}
@Override
public APPInfo getAppInfo() {
return null;
}
@Override
public String getTag() {
return TAG;
}
@Override
public Toolbar initToolBar() {
return null;
}
@Override
public boolean isEnableDisplayHomeAsUp() {
return false;
}
@Override
public boolean isAddWinBollToolBar() {
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
LogUtils.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_log);
mLogView = findViewById(R.id.logview);
mLogView.start();
}
@Override
protected void onResume() {
LogUtils.d(TAG, "onResume");
super.onResume();
}
}

View File

@@ -5,40 +5,29 @@ package cc.winboll.studio.libapputils.activities;
* @Date 2025/01/18 10:32:21 * @Date 2025/01/18 10:32:21
* @Describe 二维码扫码解码窗口 * @Describe 二维码扫码解码窗口
*/ */
import cc.winboll.studio.libapputils.R;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libapputils.app.IWinBollActivity; import androidx.core.app.ActivityCompat;
import cc.winboll.studio.libapputils.bean.APPInfo; import androidx.core.content.ContextCompat;
import cc.winboll.studio.libapputils.R;
import cc.winboll.studio.libapputils.app.WinBollActivity;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.journeyapps.barcodescanner.BarcodeCallback; import com.journeyapps.barcodescanner.BarcodeCallback;
import com.journeyapps.barcodescanner.BarcodeResult; import com.journeyapps.barcodescanner.BarcodeResult;
import com.journeyapps.barcodescanner.DecoratedBarcodeView; import com.journeyapps.barcodescanner.DecoratedBarcodeView;
import java.util.List; import java.util.List;
public class QRCodeDecodeActivity extends Activity implements IWinBollActivity { public class QRCodeDecodeActivity extends WinBollActivity {
public static final String TAG = "QRCodeDecodeActivity"; public static final String TAG = "QRCodeDecodeActivity";
public static final String EXTRA_RESULT = "EXTRA_RESULT"; public static final String EXTRA_RESULT = "EXTRA_RESULT";
private static final int REQUEST_CAMERA_PERMISSION = 1;
private static final int REQUEST_CAMERA_PERMISSION = 1;
TextView resultTextView; TextView resultTextView;
DecoratedBarcodeView barcodeView; DecoratedBarcodeView barcodeView;
@Override
public Activity getActivity() {
return this;
}
@Override
public APPInfo getAppInfo() {
return null;
}
@Override @Override
public String getTag() { public String getTag() {
@@ -46,18 +35,18 @@ public class QRCodeDecodeActivity extends Activity implements IWinBollActivity {
} }
@Override @Override
public boolean isEnableDisplayHomeAsUp() { protected boolean isEnableDisplayHomeAsUp() {
return true; return true;
} }
@Override @Override
public boolean isAddWinBollToolBar() { protected boolean isAddWinBollToolBar() {
return false; return false;
} }
@Override @Override
public Toolbar initToolBar() { protected Toolbar initToolBar() {
return findViewById(R.id.activityqrcodedecodeToolbar1); return findViewById(R.id.activityqrcodedecodeToolbar1);
} }
@@ -68,17 +57,16 @@ public class QRCodeDecodeActivity extends Activity implements IWinBollActivity {
resultTextView = findViewById(R.id.activityqrcodedecodeTextView1); resultTextView = findViewById(R.id.activityqrcodedecodeTextView1);
barcodeView = findViewById(R.id.activityqrcodedecodeDecoratedBarcodeView1); barcodeView = findViewById(R.id.activityqrcodedecodeDecoratedBarcodeView1);
// 请求相机权限 // 请求相机权限
// if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
// != PackageManager.PERMISSION_GRANTED) { != PackageManager.PERMISSION_GRANTED) {
// ActivityCompat.requestPermissions(this, ActivityCompat.requestPermissions(this,
// new String[]{android.Manifest.permission.CAMERA}, new String[]{android.Manifest.permission.CAMERA},
// REQUEST_CAMERA_PERMISSION); REQUEST_CAMERA_PERMISSION);
// } else { } else {
// startScanning(); startScanning();
// } }
startScanning();
} }
private void startScanning() { private void startScanning() {

View File

@@ -0,0 +1,240 @@
package cc.winboll.studio.libapputils.ads;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/12/09 15:37:35
* @Describe WinBoll 应用推广视图
*/
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.RelativeLayout;
import com.hjq.toast.ToastUtils;
public class ADsView extends RelativeLayout {
public static final String TAG = "ADsView";
public volatile boolean mIsHandling;
public volatile boolean mIsAddNewLog;
Context mContext;
WebView mWebView;
//ScrollView mScrollView;
//TextView mTextView;
//CheckBox mSelectableCheckBox;
//LogViewThread mLogViewThread;
//LogViewHandler mLogViewHandler;
//Spinner mLogLevelSpinner;
//ArrayAdapter<CharSequence> mLogLevelSpinnerAdapter;
public ADsView(Context context) {
super(context);
initView(context);
}
public ADsView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public ADsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public ADsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
// public void start() {
// mLogViewThread = new LogViewThread(LogView.this);
// mLogViewThread.start();
// // 显示日志
// showAndScrollLogView();
// }
//
// public void scrollLogUp() {
// mScrollView.post(new Runnable() {
// @Override
// public void run() {
// mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
// // 日志显示结束
// mLogViewHandler.setIsHandling(false);
// // 检查是否添加了新日志
// if (mLogViewHandler.isAddNewLog()) {
// // 有新日志添加,先更改新日志标志
// mLogViewHandler.setIsAddNewLog(false);
// // 再次发送显示日志的显示
// Message message = mLogViewHandler.obtainMessage(LogViewHandler.MSG_LOGVIEW_UPDATE);
// mLogViewHandler.sendMessage(message);
// }
// }
// });
// }
void initView(Context context) {
mContext = context;
// mLogViewHandler = new LogViewHandler();
// 加载视图布局
View viewMain = inflate(mContext, cc.winboll.studio.libapputils.R.layout.view_ads, null);
mWebView = viewMain.findViewById(cc.winboll.studio.libapputils.R.id.viewadsWebView1);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
ToastUtils.show(url);
return true;
}
});
// // 初始化日志子控件视图
// //
// mScrollView = findViewById(cc.winboll.studio.libapputils.R.id.viewlogScrollViewLog);
// mTextView = findViewById(cc.winboll.studio.libapputils.R.id.viewlogTextViewLog);
// // 获取Log Level spinner实例
// mLogLevelSpinner = findViewById(cc.winboll.studio.libapputils.R.id.viewlogSpinner1);
//
// (findViewById(cc.winboll.studio.libapputils.R.id.viewlogButtonClean)).setOnClickListener(new View.OnClickListener(){
//
// @Override
// public void onClick(View v) {
// LogUtils.cleanLog();
// LogUtils.d(TAG, "Log is cleaned.");
// }
// });
// (findViewById(cc.winboll.studio.libapputils.R.id.viewlogButtonCopy)).setOnClickListener(new View.OnClickListener(){
//
// @Override
// public void onClick(View v) {
//
// ClipboardManager cm = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
// cm.setPrimaryClip(ClipData.newPlainText(mContext.getPackageName(), LogUtils.loadLog()));
// LogUtils.d(TAG, "Log is copied.");
// }
// });
// mSelectableCheckBox = findViewById(cc.winboll.studio.libapputils.R.id.viewlogCheckBoxSelectable);
// mSelectableCheckBox.setOnClickListener(new View.OnClickListener(){
// @Override
// public void onClick(View v) {
// if (mSelectableCheckBox.isChecked()) {
// setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
// } else {
// setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
// }
// }
// });
//
// // 设置日志级别列表
// ArrayList<String> adapterItems = new ArrayList<>();
// for (LogUtils.LOG_LEVEL e : LogUtils.LOG_LEVEL.values()) {
// adapterItems.add(e.name());
// }
// // 假设你有一个字符串数组作为选项列表
// //String[] options = {"Option 1", "Option 2", "Option 3"};
// // 创建一个ArrayAdapter来绑定数据到spinner
// mLogLevelSpinnerAdapter = ArrayAdapter.createFromResource(
// context, cc.winboll.studio.libapputils.R.array.enum_loglevel_array, android.R.layout.simple_spinner_item);
// mLogLevelSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//
// // 设置适配器并将它应用到spinner上
// mLogLevelSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // 设置下拉视图样式
// mLogLevelSpinner.setAdapter(mLogLevelSpinnerAdapter);
// // 为Spinner添加监听器
// mLogLevelSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
// @Override
// public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// //String selectedOption = mLogLevelSpinnerAdapter.getItem(position);
// // 处理选中的选项...
// LogUtils.setLogLevel(LogUtils.LOG_LEVEL.values()[position]);
// }
// @Override
// public void onNothingSelected(AdapterView<?> parent) {
// // 如果没有选择,则执行此操作...
// }
// });
// // 获取默认值的索引
// int defaultValueIndex = LogUtils.getLogLevel().ordinal();
//
// if (defaultValueIndex != -1) {
// // 如果找到了默认值,设置默认选项
// mLogLevelSpinner.setSelection(defaultValueIndex);
// }
//
// // 设置滚动时不聚焦日志
// setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
}
public void loadUrl(String url) {
mWebView.loadUrl(url);
//webView.loadUrl("https://www.winboll.cc");
//webView.loadUrl("https://ads.winboll.cc");
}
// public void updateLogView() {
// if (mLogViewHandler.isHandling() == true) {
// // 正在处理日志显示,
// // 就先设置一个新日志标志位
// // 以便日志显示完后,再次显示新日志内容
// mLogViewHandler.setIsAddNewLog(true);
// } else {
// //LogUtils.d(TAG, "LogListener showLog(String path)");
// Message message = mLogViewHandler.obtainMessage(LogViewHandler.MSG_LOGVIEW_UPDATE);
// mLogViewHandler.sendMessage(message);
// mLogViewHandler.setIsAddNewLog(false);
// }
// }
//
// void showAndScrollLogView() {
// mTextView.setText(LogUtils.loadLog());
// scrollLogUp();
// }
//
// class LogViewHandler extends Handler {
//
// final static int MSG_LOGVIEW_UPDATE = 0;
// volatile boolean isHandling;
// volatile boolean isAddNewLog;
//
// public LogViewHandler() {
// setIsHandling(false);
// setIsAddNewLog(false);
// }
//
// public void setIsHandling(boolean isHandling) {
// this.isHandling = isHandling;
// }
//
// public boolean isHandling() {
// return isHandling;
// }
//
// public void setIsAddNewLog(boolean isAddNewLog) {
// this.isAddNewLog = isAddNewLog;
// }
//
// public boolean isAddNewLog() {
// return isAddNewLog;
// }
//
// public void handleMessage(Message msg) {
// switch (msg.what) {
// case MSG_LOGVIEW_UPDATE:{
// if (isHandling() == false) {
// setIsHandling(true);
// showAndScrollLogView();
// }
// break;
// }
// default:
// break;
// }
// super.handleMessage(msg);
// }
// }
}

View File

@@ -1,54 +0,0 @@
package cc.winboll.studio.libapputils.app;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2025/02/06 08:45:23
* @Describe 关于活动窗口的介绍窗口工厂
*/
import android.content.Context;
import android.content.Intent;
import cc.winboll.studio.libapputils.activities.AboutActivity;
import cc.winboll.studio.libapputils.app.AboutActivityFactory;
import cc.winboll.studio.libapputils.bean.APPInfo;
public class AboutActivityFactory {
public static final String TAG = "AboutActivityFactory";
public static APPInfo buildDefaultAPPInfo() {
String szBranchName = "";
APPInfo appInfo = new APPInfo();
appInfo.setAppName("APP");
appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
appInfo.setAppDescription("APP Description");
appInfo.setAppGitName("APP");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(szBranchName);
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=APP");
appInfo.setAppAPKName("APP");
appInfo.setAppAPKFolderName("APP");
return appInfo;
}
public static void showAboutActivity(Context context, APPInfo appInfo) {
/*String szPN = ((IWinBollActivity)context).getActivityPackageName();
//String szPN = context.getPackageName();
String szBranchName = "";
if (szPN != null) {
//String szPN = "cc.winboll.studio.apputils.beta";
String regex = "cc\\.winboll\\.studio\\.([^.]+)\\.*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(szPN);
if (matcher.find()) {
szBranchName = matcher.group(1);
}
}*/
//ToastUtils.show(szPN);
Intent intent = new Intent(context, AboutActivity.class);
intent.putExtra(AboutActivity.EXTRA_APPINFO, (appInfo == null) ? AboutActivityFactory.buildDefaultAPPInfo():appInfo);
WinBollActivityManager.getInstance(context).startWinBollActivity(context, intent, AboutActivity.class);
}
}

View File

@@ -5,7 +5,7 @@ package cc.winboll.studio.libapputils.app;
* @Date 2024/08/12 14:45:35 * @Date 2024/08/12 14:45:35
* @Describe 应用版本工具集 * @Describe 应用版本工具集
*/ */
import cc.winboll.studio.libappbase.LogUtils; import com.hjq.toast.ToastUtils;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -23,7 +23,6 @@ public class AppVersionUtils {
// true 新版本 == 当前版本 // true 新版本 == 当前版本
// //
public static boolean isHasNewVersion2(String szCurrentName, String szNextName) { public static boolean isHasNewVersion2(String szCurrentName, String szNextName) {
LogUtils.d(TAG, String.format("isHasNewVersion2\nszCurrentName : %s\nszNextName : %s", szCurrentName, szNextName));
//szCurrentName = "AES_6.2.0-beta0_3234.apk"; //szCurrentName = "AES_6.2.0-beta0_3234.apk";
//szNextName = "AES_6.1.12.apk"; //szNextName = "AES_6.1.12.apk";
//szCurrentName = "AES_6.2.0-beta0_3234.apk"; //szCurrentName = "AES_6.2.0-beta0_3234.apk";
@@ -78,9 +77,8 @@ public class AppVersionUtils {
//LogUtils.d(TAG, "App version is the newest. "); //LogUtils.d(TAG, "App version is the newest. ");
return false; return false;
} }
public static boolean isHasNewStageReleaseVersion(String szCurrentName, String szNextName) { public static boolean isHasNewStageReleaseVersion(String szCurrentName, String szNextName) {
LogUtils.d(TAG, String.format("isHasNewStageReleaseVersion\nszCurrentName : %s\nszNextName : %s", szCurrentName, szNextName));
//szCurrentName = "AES_6.2.12.apk"; //szCurrentName = "AES_6.2.12.apk";
//szNextName = "AES_6.3.12.apk"; //szNextName = "AES_6.3.12.apk";
if (checkNewVersion(getCodeInPackageName(szCurrentName), getCodeInPackageName(szNextName))) { if (checkNewVersion(getCodeInPackageName(szCurrentName), getCodeInPackageName(szNextName))) {
@@ -98,10 +96,6 @@ public class AppVersionUtils {
// 返回 true 新版本 > 当前版本 // 返回 true 新版本 > 当前版本
// //
public static Boolean checkNewVersion(String szCurrentCode, String szNextCode) { public static Boolean checkNewVersion(String szCurrentCode, String szNextCode) {
if (szCurrentCode == null || szCurrentCode.equals("") || szNextCode == null || szNextCode.equals("")) {
LogUtils.d(TAG, String.format("checkNewVersion unexpected parameters:\nszCurrentCode : %s\nszNextCode : %s", szCurrentCode, szNextCode));
return false;
}
boolean isNew = false; boolean isNew = false;
String[] appVersionCurrent = szCurrentCode.split("\\."); String[] appVersionCurrent = szCurrentCode.split("\\.");
String[] appVersionNext = szNextCode.split("\\."); String[] appVersionNext = szNextCode.split("\\.");
@@ -112,7 +106,7 @@ public class AppVersionUtils {
if (Integer.parseInt(appVersionNext[i]) > Integer.parseInt(appVersionCurrent[i])) { if (Integer.parseInt(appVersionNext[i]) > Integer.parseInt(appVersionCurrent[i])) {
isNew = true; isNew = true;
return isNew; return isNew;
} else if (Integer.parseInt(appVersionNext[i]) == Integer.parseInt(appVersionCurrent[i])) { } else if(Integer.parseInt(appVersionNext[i]) == Integer.parseInt(appVersionCurrent[i])) {
continue ; continue ;
} else { } else {
isNew = false; isNew = false;
@@ -128,17 +122,14 @@ public class AppVersionUtils {
// 如 AppUtils_7.0.4.apk 版本号为 7.0.4 // 如 AppUtils_7.0.4.apk 版本号为 7.0.4
// //
public static String getCodeInPackageName(String apkName) { public static String getCodeInPackageName(String apkName) {
LogUtils.d(TAG, String.format("getCodeInPackageName apkName : %s", apkName));
//String apkName = "AppUtils_7.0.0.apk"; //String apkName = "AppUtils_7.0.0.apk";
Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+"); Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+");
Matcher matcher = pattern.matcher(apkName); Matcher matcher = pattern.matcher(apkName);
if (matcher.find()) { if (matcher.find()) {
String version = matcher.group(); String version = matcher.group();
LogUtils.d(TAG, String.format("version is %s", version));
return version; return version;
//System.out.println("Version number: " + version); // 输出7.0.0 //System.out.println("Version number: " + version); // 输出7.0.0
} }
LogUtils.d(TAG, String.format("No result."));
return ""; return "";
} }
@@ -158,4 +149,5 @@ public class AppVersionUtils {
} }
return ""; return "";
} }
} }

View File

@@ -1,23 +0,0 @@
package cc.winboll.studio.libapputils.app;
/**
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/03/08 00:15:36
* @Describe WinBoll 活动窗口通用接口
*/
import android.app.Activity;
import android.widget.Toolbar;
import cc.winboll.studio.libapputils.bean.APPInfo;
public interface IWinBollActivity {
public static final String TAG = "IWinBollActivity";
// 获取当前具有 IWinBoll 特征的 AppCompatActivity 活动窗口
abstract public Activity getActivity();
abstract public APPInfo getAppInfo();
abstract public String getTag();
abstract public Toolbar initToolBar();
abstract public boolean isEnableDisplayHomeAsUp();
abstract public boolean isAddWinBollToolBar();
}

View File

@@ -6,19 +6,22 @@ package cc.winboll.studio.libapputils.app;
*/ */
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libapputils.log.LogUtils;
import cc.winboll.studio.libappbase.utils.ToastUtils; import com.hjq.toast.ToastUtils;
import java.util.Set;
public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
public static final String TAG = "MyActivityLifecycleCallbacks"; public static final String TAG = "MyActivityLifecycleCallbacks";
public String mInfo = ""; public String mInfo = "";
public MyActivityLifecycleCallbacks() { public MyActivityLifecycleCallbacks(WinBollApplication application) {
} }
void createActivityeInfo(Activity activity) { void createActivityeInfo(Activity activity) {

View File

@@ -0,0 +1,479 @@
package cc.winboll.studio.libapputils.app;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/08/12 14:32:08
* @Describe WinBoll 活动窗口基础类
*/
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import cc.winboll.studio.libapputils.R;
import cc.winboll.studio.libapputils.activities.AboutActivity;
import cc.winboll.studio.libapputils.log.LogActivity;
import cc.winboll.studio.libapputils.log.LogUtils;
import cc.winboll.studio.libapputils.view.AboutView;
import com.hjq.toast.ToastUtils;
import java.util.List;
import java.util.Set;
abstract public class WinBollActivity extends AppCompatActivity {
public static final String TAG = "WinBollActivity";
public static final int REQUEST_LOG_ACTIVITY = 0;
Toolbar mToolBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
checkResolveActivity();
LogUtils.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
archiveInstance();
}
boolean checkResolveActivity() {
PackageManager packageManager = getPackageManager();
//Intent intent = new Intent("your_action_here");
Intent intent = getIntent();
if (intent != null) {
List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfoList.size() > 0) {
// 传入的Intent action在Activity清单的intent-filter的action节点里有定义
if (intent.getAction() != null) {
if (intent.getAction().equals(cc.winboll.studio.libapputils.intent.action.DEBUGVIEW)) {
WinBollApplication.setIsDebug(true);
//ToastUtils.show!("WinBollApplication.setIsDebug(true) by action : " + intent.getAction());
}
}
return true;
} else {
// 传入的Intent action在Activity清单的intent-filter的action节点里没有定义
//ToastUtils.show("false : " + intent.getAction());
return false;
}
/* ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
if (resolveInfo != null) {
// action在清单文件中有声明
} else {
}*/
}
// action在清单文件中没有声明
ToastUtils.show("false");
return false;
}
void archiveInstance() {
Intent intent = getIntent();
StringBuilder sb = new StringBuilder("\n### Archive Instance ###\n");
if (intent != null) {
ComponentName componentName = intent.getComponent();
if (componentName != null) {
String packageName = componentName.getPackageName();
//Log.d("AppStarter", "启动本应用的应用包名: " + packageName);
sb.append("启动本应用的应用包名: \n" + packageName);
}
sb.append("\nImplicit Intent Tracker \n接收到的 Intent 动作: \n" + intent.getAction());
Set<String> categories = intent.getCategories();
if (categories != null) {
for (String category : categories) {
sb.append("\n接收到的 Intent 类别 :\n" + category);
}
}
Uri data = intent.getData();
if (data != null) {
sb.append("\n接收到的 Intent 数据 :\n" + data.toString());
}
} else {
sb.append("Intent is null.");
}
sb.append("\n\n");
LogUtils.d(TAG, sb.toString());
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mToolBar = initToolBar();
setSupportActionBar(mToolBar);
if (isEnableDisplayHomeAsUp() && mToolBar != null) {
// 显示后退按钮
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
// 缓存当前 activity
LogUtils.d(TAG, "ActManager.getInstance().add(this);");
//ToastUtils.show("getTag() " + getTag());
WinBollActivityManager.getInstance(this).add(this);
//WinBollActivityManager.getInstance().printAvtivityListInfo();
//ToastUtils.show("WinBollUI_TYPE " + WinBollApplication.getWinBollUI_TYPE());
//boolean isDebuging = WinBollApplication.loadDebugStatusIsDebuging();
//ToastUtils.show(String.valueOf(isDebuging));
//WinBollApplication.setIsDebug(true);
setSubTitle(getTag());
}
@Override
public void recreate() {
super.recreate();
}
@Override
protected void onDestroy() {
WinBollActivityManager.getInstance(this).registeRemove(this);
super.onDestroy();
}
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
}
@Override
public <T extends View> T findViewById(int id) {
return super.findViewById(id);
}
@Override
public MenuInflater getMenuInflater() {
return super.getMenuInflater();
}
public WinBollActivity getActivity() {
return this;
}
@Override
public Context getApplicationContext() {
return super.getApplicationContext();
}
public <T extends FragmentManager> T getWinBollActivitySupportFragmentManager() {
return (T)super.getSupportFragmentManager();
}
@Override
public ActionBar getSupportActionBar() {
return super.getSupportActionBar();
}
@Override
public void setSupportActionBar(Toolbar toolbar) {
super.setSupportActionBar(toolbar);
}
@Override
public void setActionBar(android.widget.Toolbar toolbar) {
super.setActionBar(toolbar);
}
public void setSubTitle(CharSequence title) {
LogUtils.d(TAG, "setSubTitle");
if (super.getSupportActionBar() != null) {
super.getSupportActionBar().setSubtitle(title);
LogUtils.d(TAG, String.format("setSubtitle title : %s", title));
}
}
public CharSequence getSubTitle() {
if (super.getSupportActionBar() != null) {
return super.getSupportActionBar().getSubtitle();
}
return "";
}
@Override
public void setTitle(CharSequence title) {
//super.setTitle(title);
if (super.getSupportActionBar() != null) {
super.getSupportActionBar().setTitle(title);
LogUtils.d(TAG, String.format("setSubtitle title : %s", title));
}
}
@Override
public void setTitle(int titleId) {
//super.setTitle(titleId);
if (super.getSupportActionBar() != null) {
super.getSupportActionBar().setTitle(titleId);
//LogUtils.d(TAG, String.format("setSubtitle title : %s", title));
}
}
@Override
public void setTheme(Resources.Theme theme) {
super.setTheme(theme);
}
@Override
public void setTheme(int resid) {
super.setTheme(resid);
}
@Override
public void setTitleColor(int textColor) {
super.setTitleColor(textColor);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
super.setContentView(view, params);
}
@Override
public void setContentView(View view) {
super.setContentView(view);
}
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return super.getSharedPreferences(name, mode);
}
@Override
public boolean releaseInstance() {
return super.releaseInstance();
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
return super.registerReceiver(receiver, filter, flags);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {
return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
return super.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags);
}
@Override
public void startActivities(Intent[] intents) {
super.startActivities(intents);
}
@Override
public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
super.startActivityFromFragment(fragment, intent, requestCode);
}
@Override
public void startActivityFromFragment(android.app.Fragment fragment, Intent intent, int requestCode, Bundle options) {
super.startActivityFromFragment(fragment, intent, requestCode, options);
}
@Override
public void startActivityFromChild(Activity child, Intent intent, int requestCode, Bundle options) {
super.startActivityFromChild(child, intent, requestCode, options);
}
@Override
public void startActivities(Intent[] intents, Bundle options) {
super.startActivities(intents, options);
}
@Override
public boolean startActivityIfNeeded(Intent intent, int requestCode) {
return super.startActivityIfNeeded(intent, requestCode);
}
@Override
public boolean startActivityIfNeeded(Intent intent, int requestCode, Bundle options) {
return super.startActivityIfNeeded(intent, requestCode, options);
}
@Override
public void startActivityFromChild(Activity child, Intent intent, int requestCode) {
super.startActivityFromChild(child, intent, requestCode);
}
@Override
public ComponentName startService(Intent service) {
return super.startService(service);
}
@Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
}
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
super.startActivityForResult(intent, requestCode, options);
}
@Override
public void startActivityFromFragment(android.app.Fragment fragment, Intent intent, int requestCode) {
super.startActivityFromFragment(fragment, intent, requestCode);
}
@Override
public void startActivityFromFragment(Fragment requestIndex, Intent fragment, int intent, Bundle requestCode) {
super.startActivityFromFragment(requestIndex, fragment, intent, requestCode);
}
@Override
public void startActivity(Intent intent, Bundle options) {
super.startActivity(intent, options);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (isAddWinBollToolBar()) {
getMenuInflater().inflate(R.menu.toolbar_winboll_shared_main, menu);
}
if (WinBollApplication.isDebug()) {
getMenuInflater().inflate(R.menu.toolbar_studio_debug, menu);
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
LogUtils.d(TAG, "onOptionsItemSelected");
if (item.getItemId() == R.id.item_log) {
LogUtils.d(TAG, "item_log not yet.");
//WinBollActivityManager.getInstance().printAvtivityListInfo();
//WinBollActivityManager.getInstance(this).startWinBollActivity(this, LogActivity.class);
} else if (item.getItemId() == R.id.item_exit) {
WinBollActivityManager.getInstance(this).finishAll();
} else if (item.getItemId() == R.id.item_info) {
WinBollApplication application = (WinBollApplication) getApplication();
application.getMyActivityLifecycleCallbacks().showActivityeInfo();
} else if (item.getItemId() == R.id.item_exitdebug) {
AboutView.setApp2NormalMode(getApplicationContext());
} else if (item.getItemId() == R.id.item_about) {
startAboutActivity();
} else if (item.getItemId() == android.R.id.home) {
WinBollActivityManager.getInstance(this).finish(this);
}
// else if (item.getItemId() == android.R.id.home) {
// 回到主窗口速度缓慢,方法备用。现在用 WinBollActivityManager resumeActivity 和 finish 方法管理。
// _mMainWinBollActivity 是 WinBollActivity 的静态属性
// onCreate 函数下 _mMainWinBollActivity 为空时就用 _mMainWinBollActivity = this 赋值。
//startWinBollActivity(new Intent(_mMainWinBollActivity, _mMainWinBollActivity.getClass()), _mMainWinBollActivity.getTag(), _mMainWinBollActivity);
//}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
//super.onBackPressed();
//ToastUtils.show("onBackPressed");
WinBollActivityManager.getInstance(this).finish(this);
}
/*public void getInstanse() {
startWinBollActivity(new Intent(), getTag(), null);
}*/
//
// activity: 为 null 时,
// intent.putExtra 函数 "tag" 参数为 tag
// activity: 不为 null 时,
// intent.putExtra 函数 "tag" 参数为 activity.getTag()
//
// protected <T extends WinBollActivity> void startWinBollActivity(Intent intent, String tag, T activity) {
// LogUtils.d(TAG, "startWinBollActivityForResult tag " + tag);
// //ToastUtils.show("startWinBollActivityForResult tag " + tag);
// //打开多任务窗口 flags
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
// intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
// if (activity != null) {
// intent.putExtra("tag", activity.getTag());
// } else {
// intent.putExtra("tag", tag);
// }
// //ToastUtils.show("super.startActivityForResult(intent, requestCode); tag " + tag);
// LogUtils.d(TAG, "startActivityForResult(intent, requestCode);" + tag);
// startActivity(intent);
// }
//
// activity: 为 null 时,
// intent.putExtra 函数 "tag" 参数为 tag
// activity: 不为 null 时,
// intent.putExtra 函数 "tag" 参数为 activity.getTag()
//
protected <T extends AboutActivity> void startAboutActivity() {
Intent intent = new Intent(this, AboutActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.putExtra("tag", AboutActivity.TAG);
startActivity(intent);
}
@Override
public void startActivity(Intent intent) {
//绑定唯一标识 tag存在 则根据 taskId 移动到前台
String tag = intent.getStringExtra("tag");
//ToastUtils.show("startActivityForResult tag " + tag);
//WinBollActivityManager.getInstance(this).printAvtivityListInfo();
if (WinBollActivityManager.getInstance(this).isActive(tag)) {
//ToastUtils.show("resumeActivity");
LogUtils.d(TAG, "resumeActivity");
WinBollActivityManager.getInstance(this).resumeActivity(this, tag);
} else {
//ToastUtils.show("super.startActivity(intent);");
super.startActivity(intent);
}
}
@Override
protected void onActivityResult(int requestCode, int targetFragment, Intent data) {
LogUtils.d(TAG, "onActivityResult");
switch (requestCode) {
case REQUEST_LOG_ACTIVITY : {
LogUtils.d(TAG, "REQUEST_LOG_ACTIVITY");
break;
}
default : {
super.onActivityResult(requestCode, targetFragment, data);
}
}
}
public boolean isAddWinBollInfoMenu() {
return true;
}
abstract public String getTag();
abstract protected Toolbar initToolBar();
abstract protected boolean isEnableDisplayHomeAsUp();
abstract protected boolean isAddWinBollToolBar();
}

View File

@@ -9,10 +9,10 @@ package cc.winboll.studio.libapputils.app;
* https://blog.csdn.net/qq_29364417/article/details/109379915?app_version=6.4.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22109379915%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app * https://blog.csdn.net/qq_29364417/article/details/109379915?app_version=6.4.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22109379915%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
*/ */
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.TaskStackBuilder;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import cc.winboll.studio.libappbase.LogUtils; import androidx.core.app.TaskStackBuilder;
import cc.winboll.studio.libapputils.log.LogUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
@@ -20,26 +20,17 @@ import java.util.Map;
public class WinBollActivityManager { public class WinBollActivityManager {
public static final String TAG = "WinBollActivityManager"; public static final String TAG = "WinBollActivityManager";
public static final String EXTRA_TAG = "EXTRA_TAG";
public static enum WinBollUI_TYPE {
Aplication, // 退出应用后,保持最近任务栏任务记录主窗口
Service // 退出应用后,清理所有最近任务栏任务记录窗口
};
// 应用类型标志
volatile static WinBollUI_TYPE _mWinBollUI_TYPE = WinBollUI_TYPE.Service;
Context mContext; Context mContext;
MyActivityLifecycleCallbacks mMyActivityLifecycleCallbacks;
static WinBollActivityManager _mWinBollActivityManager; static WinBollActivityManager _mWinBollActivityManager;
static Map<String, IWinBollActivity> _mapIWinBollList; static Map<String, WinBollActivity> _mapActivityList;
IWinBollActivity firstIWinBollActivity; //static ArrayList<WinBollActivity> _mWinBollActivityList;
public WinBollActivityManager(Context context) { public WinBollActivityManager(Context context) {
mContext = context; mContext = context;
LogUtils.d(TAG, "WinBollActivityManager()"); LogUtils.d(TAG, "WinBollActivityManager()");
_mapIWinBollList = new HashMap<String, IWinBollActivity>(); _mapActivityList = new HashMap<String, WinBollActivity>();
//_mWinBollActivityList = new ArrayList<WinBollActivity>();
} }
public static synchronized WinBollActivityManager getInstance(Context context) { public static synchronized WinBollActivityManager getInstance(Context context) {
@@ -51,46 +42,35 @@ public class WinBollActivityManager {
return _mWinBollActivityManager; return _mWinBollActivityManager;
} }
// /**
// 设置 WinBoll 应用 UI 类型 * 把Activity添加到管理中
// */
public synchronized static void setWinBollUI_TYPE(WinBollUI_TYPE mWinBollUI_TYPE) { public <T extends WinBollActivity> void add(T activity) {
_mWinBollUI_TYPE = mWinBollUI_TYPE; /*for (int i = 0; i < _mWinBollActivityList.size(); i++) {
} LogUtils.d(TAG, String.format("add for i %d\nget(i).getTag() %s", i, _mWinBollActivityList.get(i).getTag()));
if (_mWinBollActivityList.get(i).getTag().equals(activity.getTag())) {
// _mWinBollActivityList.add(i, activity);
// 获取 WinBoll 应用 UI 类型 _mWinBollActivityList.remove(i);
// LogUtils.d(TAG, String.format("Replace activity : %s\nSize %d", activity.getTag(), _mWinBollActivityList.size()));
public synchronized static WinBollUI_TYPE getWinBollUI_TYPE() { return;
return _mWinBollUI_TYPE; }
} }*/
if (isActive(activity.getTag())) {
// LogUtils.d(TAG, String.format("add(...) %s is active.", activity.getTag()));
// 把Activity添加到管理中
//
public <T extends IWinBollActivity> void add(T iWinBoll) {
if (isActive(iWinBoll.getTag())) {
LogUtils.d(TAG, String.format("add(...) %s is active.", iWinBoll.getTag()));
} else { } else {
// 设置起始活动窗口,以便最后退出时提问 _mapActivityList.put(activity.getTag(), activity);
if (firstIWinBollActivity == null && _mapIWinBollList.size() == 0) { LogUtils.d(TAG, String.format("Add activity : %s\n_mapActivityList.size() : %d", activity.getTag(), _mapActivityList.size()));
firstIWinBollActivity = iWinBoll;
}
// 添加到活动窗口列表
_mapIWinBollList.put(iWinBoll.getTag(), iWinBoll);
LogUtils.d(TAG, String.format("Add activity : %s\n_mapActivityList.size() : %d", iWinBoll.getTag(), _mapIWinBollList.size()));
} }
} }
// //
// activity: 为 null 时, // activity: 为 null 时,
// intent.putExtra 函数 EXTRA_TAG 参数为 tag // intent.putExtra 函数 "tag" 参数为 tag
// activity: 不为 null 时, // activity: 不为 null 时,
// intent.putExtra 函数 "tag" 参数为 activity.getTag() // intent.putExtra 函数 "tag" 参数为 activity.getTag()
// //
public <T extends IWinBollActivity> void startWinBollActivity(Context context, Class<T> clazz) { public <T extends WinBollActivity> void startWinBollActivity(Context context, Class<T> clazz) {
try { try {
// 如果窗口已存在就重启窗口 // 如果窗口已存在就重启窗口
String tag = clazz.newInstance().getTag(); String tag = clazz.newInstance().getTag();
@@ -104,15 +84,14 @@ public class WinBollActivityManager {
//打开多任务窗口 flags //打开多任务窗口 flags
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("tag", tag);
intent.putExtra(EXTRA_TAG, tag);
mContext.startActivity(intent); mContext.startActivity(intent);
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
} }
} }
public <T extends IWinBollActivity> void startWinBollActivity(Context context, Intent intent, Class<T> clazz) { public <T extends WinBollActivity> void startWinBollActivity(Context context, Intent intent, Class<T> clazz) {
try { try {
// 如果窗口已存在就重启窗口 // 如果窗口已存在就重启窗口
String tag = clazz.newInstance().getTag(); String tag = clazz.newInstance().getTag();
@@ -126,30 +105,25 @@ public class WinBollActivityManager {
//打开多任务窗口 flags //打开多任务窗口 flags
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("tag", tag);
intent.putExtra(EXTRA_TAG, tag);
mContext.startActivity(intent); mContext.startActivity(intent);
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
} }
} }
public boolean isFirstIWinBollActivity(IWinBollActivity iWinBollActivity) { /**
return firstIWinBollActivity != null && firstIWinBollActivity == iWinBollActivity; * 判断 tag绑定的 MyActivity是否存在
} */
//
// 判断 tag绑定的 MyActivity是否存在
//
public boolean isActive(String tag) { public boolean isActive(String tag) {
//printAvtivityListInfo(); printAvtivityListInfo();
IWinBollActivity iWinBoll = getIWinBoll(tag); WinBollActivity activity = getWinBollActivity(tag);
if (iWinBoll != null) { if (activity != null) {
LogUtils.d(TAG, "isActive(...) activity != null tag " + tag); LogUtils.d(TAG, "isActive(...) activity != null tag " + tag);
//ToastUtils.show("activity != null tag " + tag); //ToastUtils.show("activity != null tag " + tag);
//判断是否为 BaseActivity,如果已经销毁,则移除 //判断是否为 BaseActivity,如果已经销毁,则移除
if (iWinBoll.getActivity().isFinishing() || iWinBoll.getActivity().isDestroyed()) { if (activity.isFinishing() || activity.isDestroyed()) {
_mapIWinBollList.remove(iWinBoll.getTag()); _mapActivityList.remove(activity.getTag());
//_mWinBollActivityList.remove(activity); //_mWinBollActivityList.remove(activity);
LogUtils.d(TAG, String.format("isActive(...) remove activity.\ntag : %s", tag)); LogUtils.d(TAG, String.format("isActive(...) remove activity.\ntag : %s", tag));
return false; return false;
@@ -163,75 +137,75 @@ public class WinBollActivityManager {
} }
} }
static IWinBollActivity getIWinBoll(String tag) { static WinBollActivity getWinBollActivity(String tag) {
return _mapIWinBollList.get(tag); return _mapActivityList.get(tag);
} }
// /**
// 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台 * 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
// */
public <T extends IWinBollActivity> void resumeActivity(Context context, String tag) { public <T extends WinBollActivity> void resumeActivity(Context context, String tag) {
LogUtils.d(TAG, "resumeActivty"); LogUtils.d(TAG, "resumeActivty");
T iWinBoll = (T)getIWinBoll(tag); T activity = (T)getWinBollActivity(tag);
//LogUtils.d(TAG, "activity " + activity.getTag()); LogUtils.d(TAG, "activity " + activity.getTag());
if (iWinBoll != null && !iWinBoll.getActivity().isFinishing() && !iWinBoll.getActivity().isDestroyed()) { if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
resumeActivity(context, iWinBoll); resumeActivity(context, activity);
} }
} }
// /**
// 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台 * 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
// */
public <T extends IWinBollActivity> void resumeActivity(Context context, T iWinBoll) { public <T extends WinBollActivity> void resumeActivity(Context context, T activity) {
ActivityManager am = (ActivityManager) iWinBoll.getActivity().getSystemService(Context.ACTIVITY_SERVICE); ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
//返回启动它的根任务home 或者 MainActivity //返回启动它的根任务home 或者 MainActivity
Intent intent = new Intent(context, iWinBoll.getClass()); Intent intent = new Intent(context, activity.getClass());
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntentWithParentStack(intent); stackBuilder.addNextIntentWithParentStack(intent);
stackBuilder.startActivities(); stackBuilder.startActivities();
//moveTaskToFront(YourTaskId, 0); //moveTaskToFront(YourTaskId, 0);
LogUtils.d(TAG, "am.moveTaskToFront"); LogUtils.d(TAG, "am.moveTaskToFront");
//ToastUtils.show("resumeActivity am.moveTaskToFront"); //ToastUtils.show("resumeActivity am.moveTaskToFront");
am.moveTaskToFront(iWinBoll.getActivity().getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION); am.moveTaskToFront(activity.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
} }
// /**
// 结束所有 Activity * 结束所有 Activity
// */
public void finishAll() { public void finishAll() {
try { try {
for (String key : _mapIWinBollList.keySet()) { LogUtils.d(TAG, "finishAll no yet.");
//System.out.println("Key: " + key + ", Value: " + _mapActivityList.get(key)); //ToastUtils.show(String.format("finishAll() size : %d", _mWinBollActivityList.size()));
IWinBollActivity iWinBoll = _mapIWinBollList.get(key); // for (int i = _mWinBollActivityList.size() - 1; i > -1; i--) {
//ToastUtils.show("finishAll() activity"); // WinBollActivity activity = _mWinBollActivityList.get(i);
if (iWinBoll != null && !iWinBoll.getActivity().isFinishing() && !iWinBoll.getActivity().isDestroyed()) { // ToastUtils.show("finishAll() activity");
//ToastUtils.show("activity != null ..."); // if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
if (getWinBollUI_TYPE() == WinBollUI_TYPE.Service) { // //ToastUtils.show("activity != null ...");
// 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。 // if (WinBollApplication.getWinBollUI_TYPE() == WinBollApplication.WinBollUI_TYPE.Service) {
iWinBoll.getActivity().finishAndRemoveTask(); // // 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。
//ToastUtils.show("finishAll() activity.finishAndRemoveTask();"); // activity.finishAndRemoveTask();
} else if (getWinBollUI_TYPE() == WinBollUI_TYPE.Aplication) { // //ToastUtils.show("finishAll() activity.finishAndRemoveTask();");
// 结束窗口保留最近任务栏,建议前台服务类应用使用,可以保持应用的系统自觉性。 // } else if (WinBollApplication.getWinBollUI_TYPE() == WinBollApplication.WinBollUI_TYPE.Aplication) {
iWinBoll.getActivity().finish(); // // 结束窗口保留最近任务栏,建议前台服务类应用使用,可以保持应用的系统自觉性。
//ToastUtils.show("finishAll() activity.finish();"); // activity.finish();
} else { // //ToastUtils.show("finishAll() activity.finish();");
LogUtils.d(TAG, "WinBollApplication.WinBollUI_TYPE error."); // } else {
//ToastUtils.show("WinBollApplication.WinBollUI_TYPE error."); // ToastUtils.show("WinBollApplication.WinBollUI_TYPE error.");
} // }
} // }
} // }
} catch (Exception e) { } catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
} }
} }
// /**
// 结束指定Activity * 结束指定Activity
// */
public <T extends IWinBollActivity> void finish(T iWinBoll) { public <T extends WinBollActivity> void finish(T activity) {
try { try {
if (iWinBoll != null && !iWinBoll.getActivity().isFinishing() && !iWinBoll.getActivity().isDestroyed()) { if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
//根据tag 移除 MyActivity //根据tag 移除 MyActivity
//String tag= activity.getTag(); //String tag= activity.getTag();
//_mWinBollActivityList.remove(tag); //_mWinBollActivityList.remove(tag);
@@ -246,11 +220,27 @@ public class WinBollActivityManager {
// ★0 1 2 3 [4] >> 3 // ★0 1 2 3 [4] >> 3
// ★:[0] >> 直接关闭当前窗口 // ★:[0] >> 直接关闭当前窗口
LogUtils.d(TAG, "finish no yet."); LogUtils.d(TAG, "finish no yet.");
IWinBollActivity preIWinBoll = getPreIWinBoll(iWinBoll); WinBollActivity preActivity = getPreActivity(activity);
iWinBoll.getActivity().finish(); activity.finish();
if (preIWinBoll != null) { if (preActivity != null) {
resumeActivity(mContext, preIWinBoll); resumeActivity(mContext, preActivity);
} }
// for (int i = 0; i < _mWinBollActivityList.size(); i++) {
// if (_mWinBollActivityList.get(i).getTag().equals(activity.getTag())) {
// //ToastUtils.show(String.format("equals i : %d\nTag : %s\nSize : %d", i, activity.getTag(), _mWinBollActivityList.size()));
// if (i == 0) {
// finishAll();
// //ToastUtils.show("finish finishAll");
// return;
// }
// if (i > 0) {
// activity.finish();
// resumeActivity(mContext, _mWinBollActivityList.get(i - 1));
// return;
// }
// }
// }
} }
} catch (Exception e) { } catch (Exception e) {
@@ -258,24 +248,21 @@ public class WinBollActivityManager {
} }
} }
// WinBollActivity getPreActivity(WinBollActivity activity) {
// 获取窗口队列中的前一个窗口
//
IWinBollActivity getPreIWinBoll(IWinBollActivity iWinBoll) {
try { try {
boolean bingo = false; boolean bingo = false;
IWinBollActivity preIWinBoll = null; WinBollActivity preActivity = null;
for (Map.Entry<String, IWinBollActivity> entity : _mapIWinBollList.entrySet()) { for (Map.Entry<String, WinBollActivity> entity : _mapActivityList.entrySet()) {
if (entity.getKey().equals(iWinBoll.getTag())) { if (entity.getKey().equals(activity.getTag())) {
bingo = true; bingo = true;
LogUtils.d(TAG, "bingo"); LogUtils.d(TAG, "bingo");
break; break;
} }
preIWinBoll = entity.getValue(); preActivity = entity.getValue();
} }
if (bingo) { if (bingo) {
return preIWinBoll; return preActivity;
} }
} catch (Exception e) { } catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
@@ -284,28 +271,36 @@ public class WinBollActivityManager {
return null; return null;
} }
// public <T extends WinBollActivity> boolean registeRemove(T activity) {
// 从管理列表中移除管理项 // for (int i = 0; i < _mWinBollActivityList.size(); i++) {
// // if (registeRemove(activity, i)) {
public <T extends IWinBollActivity> boolean registeRemove(T activity) { // return true;
IWinBollActivity iWinBollTest = _mapIWinBollList.get(activity.getTag()); // }
if (iWinBollTest != null) { // }
_mapIWinBollList.remove(activity.getTag()); WinBollActivity activityTest = _mapActivityList.get(activity.getTag());
if (activityTest != null) {
_mapActivityList.remove(activity.getTag());
return true; return true;
} }
return false; return false;
} }
// // public <T extends WinBollActivity> boolean registeRemove(T activity, int position) {
// 打印管理列表项列表里的信息 // if (_mWinBollActivityList.get(position) == activity) {
// // _mWinBollActivityList.remove(position);
public static void printIWinBollListInfo() { // //ToastUtils.show(String.format("registeRemove remove.\nTag %s\nposition %d", activity.getTag(), position));
// return true;
// }
// return false;
// }
public static void printAvtivityListInfo() {
//LogUtils.d(TAG, "printAvtivityListInfo"); //LogUtils.d(TAG, "printAvtivityListInfo");
if (!_mapIWinBollList.isEmpty()) { if (!_mapActivityList.isEmpty()) {
StringBuilder sb = new StringBuilder("Map entries : " + Integer.toString(_mapIWinBollList.size())); StringBuilder sb = new StringBuilder("Map entries : " + Integer.toString(_mapActivityList.size()));
Iterator<Map.Entry<String, IWinBollActivity>> iterator = _mapIWinBollList.entrySet().iterator(); Iterator<Map.Entry<String, WinBollActivity>> iterator = _mapActivityList.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Map.Entry<String, IWinBollActivity> entry = iterator.next(); Map.Entry<String, WinBollActivity> entry = iterator.next();
sb.append("\nKey: " + entry.getKey() + ", \nValue: " + entry.getValue().getTag()); sb.append("\nKey: " + entry.getKey() + ", \nValue: " + entry.getValue().getTag());
//ToastUtils.show("\nKey: " + entry.getKey() + ", Value: " + entry.getValue().getTag()); //ToastUtils.show("\nKey: " + entry.getKey() + ", Value: " + entry.getValue().getTag());
} }
@@ -315,4 +310,6 @@ public class WinBollActivityManager {
LogUtils.d(TAG, "The map is empty."); LogUtils.d(TAG, "The map is empty.");
} }
} }
} }

View File

@@ -0,0 +1,76 @@
package cc.winboll.studio.libapputils.app;
import cc.winboll.studio.libapputils.bean.DebugBean;
import cc.winboll.studio.libapputils.log.LogUtils;
public class WinBollApplication extends cc.winboll.studio.libappbase.GlobalApplication {
public static final String TAG = "WinBollApplication";
public static enum WinBollUI_TYPE {
Aplication, // 退出应用后,保持最近任务栏任务记录主窗口
Service // 退出应用后,清理所有最近任务栏任务记录窗口
};
// 应用类型标志
volatile static WinBollUI_TYPE _mWinBollUI_TYPE = WinBollUI_TYPE.Service;
static volatile WinBollApplication _WinBollApplication = null;
MyActivityLifecycleCallbacks mMyActivityLifecycleCallbacks;
// 标记当前应用是否处于调试状态
static volatile boolean isDebug = false;
public static void setIsDebug(boolean isDebug) {
WinBollApplication.isDebug = isDebug;
}
public static boolean isDebug() {
return isDebug;
}
//
// 设置 WinBoll 应用 UI 类型
//
public static void setWinBollUI_TYPE(WinBollUI_TYPE mWinBollUI_TYPE) {
_mWinBollUI_TYPE = mWinBollUI_TYPE;
}
//
// 获取 WinBoll 应用 UI 类型
//
public static WinBollUI_TYPE getWinBollUI_TYPE() {
return _mWinBollUI_TYPE;
}
MyActivityLifecycleCallbacks getMyActivityLifecycleCallbacks() {
return mMyActivityLifecycleCallbacks;
}
@Override
public void onCreate() {
super.onCreate();
_WinBollApplication = this;
// 应用环境初始化, 基本调试环境
//
CrashHandler.init(this);
LogUtils.init(this);
DebugBean debugBean = DebugBean.loadBean(this, DebugBean.class);
if (debugBean == null) {
//ToastUtils.show("debugBean == null");
setIsDebug(false);
} else {
//ToastUtils.show("saveDebugStatus(" + String.valueOf(debugBean.isDebuging()) + ")");
setIsDebug(debugBean.isDebuging());
}
// 应用运行状态环境设置
//
mMyActivityLifecycleCallbacks = new MyActivityLifecycleCallbacks(this);
registerActivityLifecycleCallbacks(mMyActivityLifecycleCallbacks);
// 设置默认 WinBoll 应用 UI 类型
setWinBollUI_TYPE(WinBollUI_TYPE.Service);
//ToastUtils.show("WinBollUI_TYPE " + getWinBollUI_TYPE());
}
}

View File

@@ -1,141 +0,0 @@
package cc.winboll.studio.libapputils.bean;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2025/01/20 14:19:02
* @Describe 应用信息类
*/
import cc.winboll.studio.libapputils.R;
import java.io.Serializable;
public class APPInfo implements Serializable {
public static final String TAG = "APPInfo";
// 应用名称
String appName;
// 应用图标
int appIcon;
// 应用描述
String appDescription;
// 应用Git仓库地址
String appGitName;
// 应用Git仓库拥有者
String appGitOwner;
// 应用Git仓库分支
String appGitAPPBranch;
// 应用Git仓库子项目文件夹
String appGitAPPSubProjectFolder;
// 应用主页
String appHomePage;
// 应用包名称
String appAPKName;
// 应用包存储文件夹名称
String appAPKFolderName;
public APPInfo(String appName, int appIcon, String appDescription, String appGitName, String appGitOwner, String appGitAPPBranch, String appGitAPPSubProjectFolder, String appHomePage, String appAPKName, String appAPKFolderName) {
this.appName = appName;
this.appIcon = appIcon;
this.appDescription = appDescription;
this.appGitName = appGitName;
this.appGitOwner = appGitOwner;
this.appGitAPPBranch = appGitAPPBranch;
this.appGitAPPSubProjectFolder = appGitAPPSubProjectFolder;
this.appHomePage = appHomePage;
this.appAPKName = appAPKName;
this.appAPKFolderName = appAPKFolderName;
}
public APPInfo() {
this.appName = "WinBoll-APP";
this.appIcon = R.drawable.ic_launcher;
this.appDescription = "WinBoll APP";
this.appGitName = "APP";
this.appGitOwner = "Studio";
this.appGitAPPBranch = "app";
this.appGitAPPSubProjectFolder = "app";
this.appHomePage = "https://www.winboll.cc/studio/details.php?app=APP";
this.appAPKName = "APP";
this.appAPKFolderName = "APP";
}
public void setAppGitOwner(String appGitOwner) {
this.appGitOwner = appGitOwner;
}
public String getAppGitOwner() {
return appGitOwner;
}
public void setAppGitAPPBranch(String appGitAPPBranch) {
this.appGitAPPBranch = appGitAPPBranch;
}
public String getAppGitAPPBranch() {
return appGitAPPBranch;
}
public void setAppGitAPPSubProjectFolder(String appGitAPPSubProjectFolder) {
this.appGitAPPSubProjectFolder = appGitAPPSubProjectFolder;
}
public String getAppGitAPPSubProjectFolder() {
return appGitAPPSubProjectFolder;
}
public void setAppIcon(int appIcon) {
this.appIcon = appIcon;
}
public int getAppIcon() {
return appIcon;
}
public void setAppDescription(String appDescription) {
this.appDescription = appDescription;
}
public String getAppDescription() {
return appDescription;
}
public void setAppAPKFolderName(String appAPKFolderName) {
this.appAPKFolderName = appAPKFolderName;
}
public String getAppAPKFolderName() {
return appAPKFolderName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppName() {
return appName;
}
public void setAppGitName(String appGitName) {
this.appGitName = appGitName;
}
public String getAppGitName() {
return appGitName;
}
public void setAppHomePage(String appHomePage) {
this.appHomePage = appHomePage;
}
public String getAppHomePage() {
return appHomePage;
}
public void setAppAPKName(String appAPKName) {
this.appAPKName = appAPKName;
}
public String getAppAPKName() {
return appAPKName;
}
}

View File

@@ -10,7 +10,7 @@ package cc.winboll.studio.libapputils.bean;
import android.content.Context; import android.content.Context;
import android.util.JsonReader; import android.util.JsonReader;
import android.util.JsonWriter; import android.util.JsonWriter;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libapputils.log.LogUtils;
import cc.winboll.studio.libapputils.util.FileUtils; import cc.winboll.studio.libapputils.util.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;

View File

@@ -1,6 +1,6 @@
package cc.winboll.studio.libapputils.git; package cc.winboll.studio.libapputils.git;
import android.content.Context; import android.content.Context;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libapputils.log.LogUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;

View File

@@ -0,0 +1,66 @@
package cc.winboll.studio.libapputils.log;
import android.os.Bundle;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libapputils.R;
import cc.winboll.studio.libapputils.app.WinBollActivity;
import cc.winboll.studio.libapputils.ads.ADsView;
import cc.winboll.studio.libapputils.app.WinBollApplication;
import android.view.View;
import android.app.Activity;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/08/12 15:07:58
* @Describe WinBoll 应用日志窗口
*/
public class LogActivity extends Activity {
public static final String TAG = "LogActivity";
LogView mLogView;
//ADsView mADsView;
// @Override
// protected boolean isEnableDisplayHomeAsUp() {
// return false;
// }
@Override
protected void onCreate(Bundle savedInstanceState) {
LogUtils.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_log);
mLogView = findViewById(R.id.logview);
//mADsView = findViewById(R.id.adsview);
//mADsView.loadUrl("https://www.winboll.cc");
//mLogView.setVisibility(WinBollApplication.isDebug()?View.GONE:View.VISIBLE);
//mADsView.setVisibility(WinBollApplication.isDebug()?View.GONE:View.VISIBLE);
if(WinBollApplication.isDebug()) { mLogView.start(); }
}
@Override
protected void onResume() {
LogUtils.d(TAG, "onResume");
super.onResume();
mLogView.start();
}
// @Override
// protected boolean isAddWinBollToolBar() {
// return false;
// }
//
// @Override
// protected Toolbar initToolBar() {
// LogUtils.d(TAG, "initToolBar");
// return null;
// }
//
// @Override
// public String getTag() {
// LogUtils.d(TAG, "getTag");
// return TAG;
// }
}

View File

@@ -0,0 +1,370 @@
package cc.winboll.studio.libapputils.log;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/08/12 13:44:06
* @Describe LogUtils
* @Describe 应用日志类
*/
import android.content.Context;
import cc.winboll.studio.libapputils.app.WinBollApplication;
import cc.winboll.studio.libapputils.util.FileUtils;
import dalvik.system.DexFile;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class LogUtils {
public static final String TAG = "LogUtils";
public static enum LOG_LEVEL { Off, Error, Warn, Info, Debug, Verbose }
static volatile boolean _IsInited = false;
static Context _mContext;
// 日志显示时间格式
static SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("[yyyyMMdd_HHmmSS]", Locale.getDefault());
// 应用日志文件夹
static File _mfLogCacheDir;
static File _mfLogDataDir;
// 应用日志文件
static File _mfLogCatchFile;
static File _mfLogUtilsBeanFile;
static LogUtilsBean _mLogUtilsBean;
public static Map<String, Boolean> mapTAGList = new HashMap<String, Boolean>();
//
// 初始化函数
//
public static void init(Context context) {
_mContext = context;
init(context, LOG_LEVEL.Off);
}
//
// 初始化函数
//
public static void init(Context context, LOG_LEVEL logLevel) {
if (WinBollApplication.isDebug()) {
// 初始化日志缓存文件路径
_mfLogCacheDir = new File(context.getApplicationContext().getExternalCacheDir(), TAG);
if (!_mfLogCacheDir.exists()) {
_mfLogCacheDir.mkdirs();
}
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
// 初始化日志配置文件路径
_mfLogDataDir = context.getApplicationContext().getExternalFilesDir(TAG);
if (!_mfLogDataDir.exists()) {
_mfLogDataDir.mkdirs();
}
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
} else {
// 初始化日志缓存文件路径
_mfLogCacheDir = new File(context.getApplicationContext().getCacheDir(), TAG);
if (!_mfLogCacheDir.exists()) {
_mfLogCacheDir.mkdirs();
}
_mfLogCatchFile = new File(_mfLogCacheDir, "log.txt");
// 初始化日志配置文件路径
_mfLogDataDir = new File(context.getApplicationContext().getFilesDir(), TAG);
if (!_mfLogDataDir.exists()) {
_mfLogDataDir.mkdirs();
}
_mfLogUtilsBeanFile = new File(_mfLogDataDir, TAG + ".json");
}
// Toast.makeText(context,
// "_mfLogUtilsBeanFile : " + _mfLogUtilsBeanFile
// + "\n_mfLogCatchFile : " + _mfLogCatchFile,
// Toast.LENGTH_SHORT).show();
//
_mLogUtilsBean = LogUtilsBean.loadBeanFromFile(_mfLogUtilsBeanFile.getPath(), LogUtilsBean.class);
if (_mLogUtilsBean == null) {
_mLogUtilsBean = new LogUtilsBean();
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
}
// 加载当前应用下的所有类的 TAG
addClassTAGList();
loadTAGBeanSettings();
_IsInited = true;
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
}
public static Map<String, Boolean> getMapTAGList() {
return mapTAGList;
}
static void loadTAGBeanSettings() {
ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>();
LogUtilsClassTAGBean.loadBeanList(_mContext, list, LogUtilsClassTAGBean.class);
for (int i = 0; i < list.size(); i++) {
LogUtilsClassTAGBean beanSetting = list.get(i);
for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
if (entry.getKey().equals(beanSetting.getTag())) {
entry.setValue(beanSetting.getEnable());
}
}
}
}
static void saveTAGBeanSettings() {
ArrayList<LogUtilsClassTAGBean> list = new ArrayList<LogUtilsClassTAGBean>();
for (Map.Entry<String, Boolean> entry : mapTAGList.entrySet()) {
list.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue()));
}
LogUtilsClassTAGBean.saveBeanList(_mContext, list, LogUtilsClassTAGBean.class);
}
static void addClassTAGList() {
//ClassLoader classLoader = getClass().getClassLoader();
try {
//String packageName = context.getPackageName();
String packageNamePrefix = "cc.winboll.studio";
List<String> classNames = new ArrayList<>();
String apkPath = _mContext.getPackageCodePath();
//Log.d("APK_PATH", "The APK path is: " + apkPath);
LogUtils.d(TAG, String.format("apkPath : %s", apkPath));
//String apkPath = "/data/app/" + packageName + "-";
//DexFile dexfile = new DexFile(apkPath + "1/base.apk");
DexFile dexfile = new DexFile(apkPath);
int countTemp = 0;
Enumeration<String> entries = dexfile.entries();
while (entries.hasMoreElements()) {
countTemp++;
String className = entries.nextElement();
if (className.startsWith(packageNamePrefix)) {
classNames.add(className);
}
}
LogUtils.d(TAG, String.format("countTemp : %d\nClassNames size : %d", countTemp, classNames.size()));
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers()) && field.getType() == String.class && "TAG".equals(field.getName())) {
String tagValue = (String) field.get(null);
//Log.d("TAG_INFO", "Class: " + className + ", TAG value: " + tagValue);
//LogUtils.d(TAG, String.format("Tag Value : %s", tagValue));
//mapTAGList.put(tagValue, true);
mapTAGList.put(tagValue, false);
}
}
} catch (ClassNotFoundException | IllegalAccessException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
//Toast.makeText(context, TAG + " : " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
//Toast.makeText(context, TAG + " : " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public static void setTAGListEnable(String tag, boolean isEnable) {
Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Boolean> entry = iterator.next();
if (tag.equals(entry.getKey())) {
entry.setValue(isEnable);
//System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
break;
}
}
saveTAGBeanSettings();
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
}
public static void setALlTAGListEnable(boolean isEnable) {
Iterator<Map.Entry<String, Boolean>> iterator = mapTAGList.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Boolean> entry = iterator.next();
entry.setValue(isEnable);
//System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
saveTAGBeanSettings();
LogUtils.d(TAG, String.format("mapTAGList : %s", mapTAGList.toString()));
}
public static void setLogLevel(LOG_LEVEL logLevel) {
LogUtils._mLogUtilsBean.setLogLevel(logLevel);
_mLogUtilsBean.saveBeanToFile(_mfLogUtilsBeanFile.getPath(), _mLogUtilsBean);
}
public static LOG_LEVEL getLogLevel() {
return LogUtils._mLogUtilsBean.getLogLevel();
}
static boolean isLoggable(String tag, LOG_LEVEL logLevel) {
return _IsInited && mapTAGList.get(tag) && isInTheLevel(logLevel);
}
static boolean isInTheLevel(LOG_LEVEL logLevel) {
return (LogUtils._mLogUtilsBean.getLogLevel().ordinal() == logLevel.ordinal()
|| LogUtils._mLogUtilsBean.getLogLevel().ordinal() > logLevel.ordinal());
}
//
// 获取应用日志文件夹
//
public static File getLogCacheDir() {
return _mfLogCacheDir;
}
//
// 调试日志写入函数
//
public static void e(String szTAG, String szMessage) {
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Error)) {
saveLog(szTAG, LogUtils.LOG_LEVEL.Error, szMessage);
}
}
//
// 调试日志写入函数
//
public static void w(String szTAG, String szMessage) {
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Warn)) {
saveLog(szTAG, LogUtils.LOG_LEVEL.Warn, szMessage);
}
}
//
// 调试日志写入函数
//
public static void i(String szTAG, String szMessage) {
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Info)) {
saveLog(szTAG, LogUtils.LOG_LEVEL.Info, szMessage);
}
}
//
// 调试日志写入函数
//
public static void d(String szTAG, String szMessage) {
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, szMessage);
}
}
//
// 调试日志写入函数
// 包含线程调试堆栈信息
//
public static void d(String szTAG, String szMessage, StackTraceElement[] listStackTrace) {
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
StringBuilder sbMessage = new StringBuilder(szMessage);
sbMessage.append(" \nAt ");
sbMessage.append(listStackTrace[2].getMethodName());
sbMessage.append(" (");
sbMessage.append(listStackTrace[2].getFileName());
sbMessage.append(":");
sbMessage.append(listStackTrace[2].getLineNumber());
sbMessage.append(")");
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
}
}
//
// 调试日志写入函数
// 包含异常信息和线程调试堆栈信息
//
public static void d(String szTAG, Exception e, StackTraceElement[] listStackTrace) {
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Debug)) {
StringBuilder sbMessage = new StringBuilder(e.getClass().toGenericString());
sbMessage.append(" : ");
sbMessage.append(e.getMessage());
sbMessage.append(" \nAt ");
sbMessage.append(listStackTrace[2].getMethodName());
sbMessage.append(" (");
sbMessage.append(listStackTrace[2].getFileName());
sbMessage.append(":");
sbMessage.append(listStackTrace[2].getLineNumber());
sbMessage.append(")");
saveLog(szTAG, LogUtils.LOG_LEVEL.Debug, sbMessage.toString());
}
}
//
// 调试日志写入函数
//
public static void v(String szTAG, String szMessage) {
if (isLoggable(szTAG, LogUtils.LOG_LEVEL.Verbose)) {
saveLog(szTAG, LogUtils.LOG_LEVEL.Verbose, szMessage);
}
}
//
// 日志文件保存函数
//
static void saveLog(String szTAG, LogUtils.LOG_LEVEL logLevel, String szMessage) {
try {
BufferedWriter out = null;
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_mfLogCatchFile, true), "UTF-8"));
out.write("[" + logLevel + "] " + mSimpleDateFormat.format(System.currentTimeMillis()) + " [" + szTAG + "]\n" + szMessage + "\n");
out.close();
} catch (IOException e) {
LogUtils.d(TAG, "IOException : " + e.getMessage());
}
}
//
// 历史日志加载函数
//
public static String loadLog() {
if (_mfLogCatchFile.exists()) {
StringBuffer sb = new StringBuffer();
try {
BufferedReader in = null;
in = new BufferedReader(new InputStreamReader(new FileInputStream(_mfLogCatchFile), "UTF-8"));
String line = "";
while ((line = in.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
} catch (IOException e) {
LogUtils.d(TAG, "IOException : " + e.getMessage());
}
return sb.toString();
}
return "";
}
//
// 清理日志函数
//
public static void cleanLog() {
if (_mfLogCatchFile.exists()) {
try {
FileUtils.writeStringToFile(_mfLogCatchFile.getPath(), "");
//LogUtils.d(TAG, "cleanLog");
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
}
}
}

View File

@@ -0,0 +1,71 @@
package cc.winboll.studio.libapputils.log;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/08/23 15:39:07
* @Describe LogUtils 数据配置类。
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libapputils.bean.BaseBean;
import java.io.IOException;
public class LogUtilsBean extends BaseBean {
public static final String TAG = "LogUtilsBean";
LogUtils.LOG_LEVEL logLevel;
public LogUtilsBean() {
this.logLevel = LogUtils.LOG_LEVEL.Off;
}
public LogUtilsBean(LogUtils.LOG_LEVEL logLevel) {
this.logLevel = logLevel;
}
public void setLogLevel(LogUtils.LOG_LEVEL logLevel) {
this.logLevel = logLevel;
}
public LogUtils.LOG_LEVEL getLogLevel() {
return logLevel;
}
@Override
public String getName() {
return LogUtilsBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
LogUtilsBean bean = this;
jsonWriter.name("logLevel").value(bean.getLogLevel().ordinal());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("logLevel")) {
setLogLevel(LogUtils.LOG_LEVEL.values()[jsonReader.nextInt()]);
} 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,88 @@
package cc.winboll.studio.libapputils.log;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2025/01/04 14:17:02
* @Describe 日志类class TAG 标签数据类
*/
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.libapputils.bean.BaseBean;
import java.io.IOException;
public class LogUtilsClassTAGBean extends BaseBean {
public static final String TAG = "LogUtilsClassTAGBean";
// 标签名
String tag;
// 是否启用
Boolean enable;
public LogUtilsClassTAGBean() {
this.tag = TAG;
this.enable = true;
}
public LogUtilsClassTAGBean(String tag, Boolean enable) {
this.tag = tag;
this.enable = enable;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getTag() {
return tag;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public Boolean getEnable() {
return enable;
}
@Override
public String getName() {
return LogUtilsClassTAGBean.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
LogUtilsClassTAGBean bean = this;
jsonWriter.name("tag").value(bean.getTag());
jsonWriter.name("enable").value(bean.getEnable());
}
@Override
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("tag")) {
setTag(jsonReader.nextString());
} else if (name.equals("enable")) {
setEnable(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;
}
}

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