diff --git a/contacts/build.gradle b/contacts/build.gradle
index 55ef8d2..7efb0a9 100644
--- a/contacts/build.gradle
+++ b/contacts/build.gradle
@@ -51,6 +51,8 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.squareup.okhttp3:okhttp:4.4.1'
+
// https://mvnrepository.com/artifact/com.github.open-android/pinyin4j
implementation 'com.github.open-android:pinyin4j:2.5.0'
diff --git a/contacts/build.properties b/contacts/build.properties
index 9cd1345..3feb87e 100644
--- a/contacts/build.properties
+++ b/contacts/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Wed Feb 26 19:10:28 HKT 2025
+#Sun Mar 02 10:24:46 GMT 2025
stageCount=5
libraryProject=
baseVersion=1.0
publishVersion=1.0.4
-buildCount=0
+buildCount=40
baseBetaVersion=1.0.5
diff --git a/contacts/src/main/AndroidManifest.xml b/contacts/src/main/AndroidManifest.xml
index 3b27a29..f92009a 100644
--- a/contacts/src/main/AndroidManifest.xml
+++ b/contacts/src/main/AndroidManifest.xml
@@ -183,6 +183,8 @@
+
+
-
+
\ No newline at end of file
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/App.java b/contacts/src/main/java/cc/winboll/studio/contacts/App.java
index 6f482d5..9c2044a 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/App.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/App.java
@@ -8,6 +8,7 @@ package cc.winboll.studio.contacts;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libapputils.app.WinBollActivityManager;
+import com.hjq.toast.ToastUtils;
public class App extends GlobalApplication {
@@ -23,6 +24,8 @@ public class App extends GlobalApplication {
WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
LogUtils.d(TAG, "onCreate");
+
+ ToastUtils.init(this);
}
}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java
index 4525b54..f60dde8 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java
@@ -4,7 +4,6 @@ package cc.winboll.studio.contacts.activities;
* @Author ZhanGSKen@AliYun.Com
* @Date 2025/02/21 05:37:42
*/
-import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -15,21 +14,29 @@ import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.SeekBar;
import android.widget.Switch;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
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.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.bobulltoon.TomCat;
+import cc.winboll.studio.contacts.dun.Rules;
+import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.libappbase.IWinBollActivity;
import cc.winboll.studio.libappbase.bean.APPInfo;
import com.hjq.toast.ToastUtils;
import java.lang.reflect.Field;
-import android.widget.SeekBar;
-import android.widget.TextView;
-import cc.winboll.studio.contacts.beans.MainServiceBean;
-import cc.winboll.studio.contacts.services.MainService;
+import java.util.List;
public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
@@ -43,7 +50,10 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
int mnStreamVolume;
Switch mswMainService;
-
+ private RecyclerView recyclerView;
+ private PhoneConnectRuleAdapter adapter;
+ private List ruleList;
+
@Override
public APPInfo getAppInfo() {
return null;
@@ -146,12 +156,26 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
// 当停止拖动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);
}
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 onDefaultPhone(View view) {
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
startActivity(intent);
@@ -167,6 +191,34 @@ 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!");
+ }
+ }
+ }).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() {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("允许显示悬浮框")
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/activities/UnitTestActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/activities/UnitTestActivity.java
new file mode 100644
index 0000000..7088462
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/activities/UnitTestActivity.java
@@ -0,0 +1,81 @@
+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;
+
+/**
+ * @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 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)));
+
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/adapters/MyPagerAdapter2.java b/contacts/src/main/java/cc/winboll/studio/contacts/adapters/MyPagerAdapter2.java
deleted file mode 100644
index 104f516..0000000
--- a/contacts/src/main/java/cc/winboll/studio/contacts/adapters/MyPagerAdapter2.java
+++ /dev/null
@@ -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.CallLogFragment;
-import cc.winboll.studio.contacts.fragments.ContactsFragment;
-import cc.winboll.studio.contacts.fragments.LogFragment;
-
-public class MyPagerAdapter2 extends FragmentPagerAdapter {
- public static final String TAG = "MyPagerAdapter";
-
- private static final int PAGE_COUNT = 3;
-
- public MyPagerAdapter2(@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 CallLogFragment.newInstance(position);
- }
- }
-
- @Override
- public int getCount() {
- return PAGE_COUNT;
- }
-}
-
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/adapters/PhoneConnectRuleAdapter.java b/contacts/src/main/java/cc/winboll/studio/contacts/adapters/PhoneConnectRuleAdapter.java
new file mode 100644
index 0000000..93d1165
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/adapters/PhoneConnectRuleAdapter.java
@@ -0,0 +1,123 @@
+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.List;
+
+public class PhoneConnectRuleAdapter extends RecyclerView.Adapter {
+
+ 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 ruleList;
+
+ public PhoneConnectRuleAdapter(Context context, List 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(view);
+ } else {
+ View view = inflater.inflate(R.layout.view_phone_connect_rule, parent, false);
+ return new EditViewHolder(view);
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
+ final PhoneConnectRuleModel model = ruleList.get(position);
+ if (holder instanceof SimpleViewHolder) {
+ SimpleViewHolder simpleViewHolder = (SimpleViewHolder) holder;
+ simpleViewHolder.textView.setText(model.getRuleText());
+ simpleViewHolder.button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ model.setIsSimpleView(false);
+ notifyItemChanged(position);
+ }
+ });
+ } 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 {
+ TextView textView;
+ Button button;
+
+ public SimpleViewHolder(@NonNull View itemView) {
+ super(itemView);
+ textView = itemView.findViewById(R.id.text_view);
+ button = itemView.findViewById(R.id.button);
+ }
+ }
+
+ static class EditViewHolder extends RecyclerView.ViewHolder {
+ EditText editText;
+ CheckBox checkBoxAllow;
+ CheckBox checkBoxEnable;
+ Button buttonConfirm;
+
+ public EditViewHolder(@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);
+ }
+ }
+}
+
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneBlackRuleBean.java b/contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneConnectRuleModel.java
similarity index 64%
rename from contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneBlackRuleBean.java
rename to contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneConnectRuleModel.java
index 69550cc..3975902 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneBlackRuleBean.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/beans/PhoneConnectRuleModel.java
@@ -10,21 +10,35 @@ import android.util.JsonWriter;
import cc.winboll.studio.libappbase.BaseBean;
import java.io.IOException;
-public class PhoneBlackRuleBean extends BaseBean {
-
- public static final String TAG = "PhoneBlackRuleBean";
-
+public class PhoneConnectRuleModel extends BaseBean {
+
+ public static final String TAG = "PhoneConnectRuleModel";
+
String ruleText;
+ boolean isAllowConnection;
boolean isEnable;
-
- public PhoneBlackRuleBean() {
+ boolean isSimpleView;
+
+ public PhoneConnectRuleModel() {
this.ruleText = "";
+ this.isAllowConnection = 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.isAllowConnection = isAllowConnection;
this.isEnable = isEnable;
+ this.isSimpleView = true;
+ }
+
+ public void setIsSimpleView(boolean isSimpleView) {
+ this.isSimpleView = isSimpleView;
+ }
+
+ public boolean isSimpleView() {
+ return isSimpleView;
}
public void setRuleText(String ruleText) {
@@ -35,6 +49,14 @@ public class PhoneBlackRuleBean extends BaseBean {
return ruleText;
}
+ public void setIsAllowConnection(boolean isAllowConnection) {
+ this.isAllowConnection = isAllowConnection;
+ }
+
+ public boolean isAllowConnection() {
+ return isAllowConnection;
+ }
+
public void setIsEnable(boolean isEnable) {
this.isEnable = isEnable;
}
@@ -43,17 +65,19 @@ public class PhoneBlackRuleBean extends BaseBean {
return isEnable;
}
+
+
@Override
public String getName() {
- return PhoneBlackRuleBean.class.getName();
+ return PhoneConnectRuleModel.class.getName();
}
@Override
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
super.writeThisToJsonWriter(jsonWriter);
jsonWriter.name("ruleText").value(getRuleText());
+ jsonWriter.name("isAllowConnection").value(isAllowConnection());
jsonWriter.name("isEnable").value(isEnable());
-
}
@Override
@@ -61,6 +85,8 @@ public class PhoneBlackRuleBean extends BaseBean {
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
if (name.equals("ruleText")) {
setRuleText(jsonReader.nextString());
+ } else if (name.equals("isAllowConnection")) {
+ setIsAllowConnection(jsonReader.nextBoolean());
} else if (name.equals("isEnable")) {
setIsEnable(jsonReader.nextBoolean());
} else {
@@ -83,6 +109,6 @@ public class PhoneBlackRuleBean extends BaseBean {
jsonReader.endObject();
return this;
}
-
-
+
+
}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/bobulltoon/TomCat.java b/contacts/src/main/java/cc/winboll/studio/contacts/bobulltoon/TomCat.java
new file mode 100644
index 0000000..81affee
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/bobulltoon/TomCat.java
@@ -0,0 +1,142 @@
+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 listPhoneBoBullToon = new ArrayList();
+
+ 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);
+ }
+ }
+
+ // 解压 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();
+ }
+ }
+
+ // 删除临时 ZIP 文件
+ tempZipFile.delete();
+ }
+ }
+
+ public boolean downloadBoBullToon() {
+ String zipUrl = "https://gitea.winboll.cc//Studio/BoBullToon/archive/main.zip"; // 替换为实际的 ZIP 文件 URL
+ String destinationFolder = getWorkingFolder().getPath(); // 替换为实际的目标文件夹路径
+ try {
+ downloadAndExtractZip(zipUrl, destinationFolder);
+ LogUtils.d(TAG, "ZIP 文件下载并解压成功。");
+ return true;
+ } catch (IOException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ }
+ return false;
+ }
+
+ 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;
+ }
+}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
index a4f9a1e..cc729db 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
@@ -5,25 +5,28 @@ package cc.winboll.studio.contacts.dun;
* @Date 2025/02/21 06:15:10
* @Describe 云盾防御规则
*/
-import cc.winboll.studio.contacts.beans.PhoneBlackRuleBean;
+import android.content.Context;
+import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel;
+import cc.winboll.studio.contacts.services.MainService;
+import cc.winboll.studio.contacts.utils.RegexPPiUtils;
+import cc.winboll.studio.libappbase.LogUtils;
import java.util.ArrayList;
import java.util.regex.Pattern;
-import android.content.Context;
public class Rules {
public static final String TAG = "Rules";
- ArrayList _PhoneBlacRuleBeanList;
+ ArrayList _PhoneConnectRuleModelList;
static volatile Rules _Rules;
Context mContext;
Rules(Context context) {
mContext = context;
- _PhoneBlacRuleBeanList = new ArrayList();
- PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
-
+ _PhoneConnectRuleModelList = new ArrayList();
+ loadRules();
}
+
public static synchronized Rules getInstance(Context context) {
if (_Rules == null) {
_Rules = new Rules(context);
@@ -31,46 +34,47 @@ public class Rules {
return _Rules;
}
+ public void loadRules() {
+ _PhoneConnectRuleModelList.clear();
+ PhoneConnectRuleModel.loadBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class);
+ }
+
+ public void saveRules() {
+ PhoneConnectRuleModel.saveBeanList(mContext, _PhoneConnectRuleModelList, PhoneConnectRuleModel.class);
+ }
+
public boolean isAllowed(String phoneNumber) {
- // 黑名单拒接
- for (int i = 0; i < _PhoneBlacRuleBeanList.size(); i++) {
- if (_PhoneBlacRuleBeanList.get(i).isEnable()) {
- String regex = _PhoneBlacRuleBeanList.get(i).getRuleText();
+ // 正则运算预防针
+ if (!RegexPPiUtils.isPPiOK(phoneNumber)) {
+ LogUtils.d(TAG, "RegexPPiUtils.isPPiOK return false.");
+ return false;
+ }
+ // 检验拨不通号码群
+ if (MainService.isPhoneInBoBullToon(phoneNumber)) {
+ LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
+ return false;
+ }
+
+ // 正则匹配规则名单校验
+ for (int i = 0; i < _PhoneConnectRuleModelList.size(); i++) {
+ if (_PhoneConnectRuleModelList.get(i).isEnable()) {
+ String regex = _PhoneConnectRuleModelList.get(i).getRuleText();
if (Pattern.matches(regex, phoneNumber)) {
- return false;
+ LogUtils.d(TAG, String.format("phoneNumber :%s \nisAllowConnection %s By Rule : %s", phoneNumber, _PhoneConnectRuleModelList.get(i).isAllowConnection(), _PhoneConnectRuleModelList.get(i)));
+ return _PhoneConnectRuleModelList.get(i).isAllowConnection();
}
}
}
- // 手机号码允许
- // 中国手机号码正则表达式,以1开头,第二位可以是3、4、5、6、7、8、9,后面跟9位数字
- String regex = "^1[3-9]\\d{9}$";
- if (Pattern.matches(regex, phoneNumber)) {
- return true;
- }
-
- // 指定区号号码允许
- regex = "^0660\\d+$";
- if (Pattern.matches(regex, phoneNumber)) {
- return true;
- }
-
- // 指定区号号码允许
- regex = "^020\\d+$";
- if (Pattern.matches(regex, phoneNumber)) {
- return true;
- }
-
- // 其他拒接
- return false;
+ // 其他默认接收
+ return true;
}
- public void add(String phoneRuleBlack, boolean isEnable) {
- _PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable));
- PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
+ public void add(String szPhoneConnectRule, boolean isAllowConnection, boolean isEnable) {
+ _PhoneConnectRuleModelList.add(new PhoneConnectRuleModel(szPhoneConnectRule, isAllowConnection, isEnable));
}
- public ArrayList getPhoneBlacRuleBeanList() {
- return _PhoneBlacRuleBeanList;
+ public ArrayList getPhoneBlacRuleBeanList() {
+ return _PhoneConnectRuleModelList;
}
}
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java
index 0206a1d..356201e 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java
@@ -104,6 +104,7 @@ public class ContactsFragment extends Fragment {
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));
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/services/MainService.java b/contacts/src/main/java/cc/winboll/studio/contacts/services/MainService.java
index d2c39b3..5c1eb9d 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/services/MainService.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/services/MainService.java
@@ -31,6 +31,7 @@ import cc.winboll.studio.libappbase.SOS;
import cc.winboll.studio.libappbase.bean.APPSOSBean;
import java.util.Timer;
import java.util.TimerTask;
+import cc.winboll.studio.contacts.bobulltoon.TomCat;
public class MainService extends Service {
@@ -50,6 +51,7 @@ public class MainService extends Service {
boolean isBound = false;
MainReceiver mMainReceiver;
Timer mStreamVolumeCheckTimer;
+ static volatile TomCat _TomCat;
@Override
public IBinder onBind(Intent intent) {
@@ -128,6 +130,12 @@ public class MainService extends Service {
// 召唤 WinBoll APP 绑定本服务
SOS.bindToAPPService(this, new APPSOSBean(getPackageName(), MainService.class.getName()));
+ // 初始化服务运行参数
+ _TomCat = TomCat.getInstance(this);
+ if (!_TomCat.loadPhoneBoBullToon()) {
+ LogUtils.d(TAG, "没有下载 BoBullToon 数据。BoBullToon 参数无法加载。");
+ }
+
if (mMainReceiver == null) {
// 注册广播接收器
mMainReceiver = new MainReceiver(this);
@@ -135,8 +143,6 @@ public class MainService extends Service {
}
Rules.getInstance(this);
- //Rules.getInstance(this).add("18888888888", true);
- //Rules.getInstance(this).add("16769764848", true);
startPhoneCallListener();
@@ -146,6 +152,14 @@ public class MainService extends Service {
}
}
+ public static boolean isPhoneInBoBullToon(String phone) {
+ if (_TomCat != null) {
+ return _TomCat.isPhoneBoBullToon(phone);
+ }
+ return false;
+ }
+
+
// 唤醒和绑定守护进程
//
void wakeupAndBindAssistant() {
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/utils/RegexPPiUtils.java b/contacts/src/main/java/cc/winboll/studio/contacts/utils/RegexPPiUtils.java
new file mode 100644
index 0000000..c00a9f2
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/utils/RegexPPiUtils.java
@@ -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();
+ }
+}
diff --git a/contacts/src/main/res/drawable/recycler_view_border.xml b/contacts/src/main/res/drawable/recycler_view_border.xml
new file mode 100644
index 0000000..d538cab
--- /dev/null
+++ b/contacts/src/main/res/drawable/recycler_view_border.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/activity_settings.xml b/contacts/src/main/res/layout/activity_settings.xml
index c68fcc2..c5a7085 100644
--- a/contacts/src/main/res/layout/activity_settings.xml
+++ b/contacts/src/main/res/layout/activity_settings.xml
@@ -90,6 +90,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/activity_unittest.xml b/contacts/src/main/res/layout/activity_unittest.xml
new file mode 100644
index 0000000..34b8ebf
--- /dev/null
+++ b/contacts/src/main/res/layout/activity_unittest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/view_phone_connect_rule.xml b/contacts/src/main/res/layout/view_phone_connect_rule.xml
new file mode 100644
index 0000000..5e03125
--- /dev/null
+++ b/contacts/src/main/res/layout/view_phone_connect_rule.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/contacts/src/main/res/layout/view_phone_connect_rule_simple.xml b/contacts/src/main/res/layout/view_phone_connect_rule_simple.xml
new file mode 100644
index 0000000..b34f9e0
--- /dev/null
+++ b/contacts/src/main/res/layout/view_phone_connect_rule_simple.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+