完成电话连接规则的编辑和保存
This commit is contained in:
parent
02673d19dd
commit
c827b2cbad
@ -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'
|
||||
|
||||
|
@ -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
|
||||
|
@ -183,6 +183,8 @@
|
||||
|
||||
</provider>
|
||||
|
||||
<activity android:name="cc.winboll.studio.contacts.activities.UnitTestActivity"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,6 +50,9 @@ public class SettingsActivity extends AppCompatActivity implements IWinBollActiv
|
||||
int mnStreamVolume;
|
||||
Switch mswMainService;
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private PhoneConnectRuleAdapter adapter;
|
||||
private List<PhoneConnectRuleModel> ruleList;
|
||||
|
||||
@Override
|
||||
public APPInfo getAppInfo() {
|
||||
@ -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("允许显示悬浮框")
|
||||
|
@ -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)));
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<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(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,21 +10,35 @@ import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
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;
|
||||
boolean isAllowConnection;
|
||||
boolean isEnable;
|
||||
boolean isSimpleView;
|
||||
|
||||
public PhoneBlackRuleBean() {
|
||||
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 {
|
@ -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<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);
|
||||
}
|
||||
}
|
||||
|
||||
// 解压 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;
|
||||
}
|
||||
}
|
@ -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<PhoneBlackRuleBean> _PhoneBlacRuleBeanList;
|
||||
ArrayList<PhoneConnectRuleModel> _PhoneConnectRuleModelList;
|
||||
static volatile Rules _Rules;
|
||||
Context mContext;
|
||||
|
||||
Rules(Context context) {
|
||||
mContext = context;
|
||||
_PhoneBlacRuleBeanList = new ArrayList<PhoneBlackRuleBean>();
|
||||
PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
|
||||
|
||||
_PhoneConnectRuleModelList = new ArrayList<PhoneConnectRuleModel>();
|
||||
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 (Pattern.matches(regex, phoneNumber)) {
|
||||
// 正则运算预防针
|
||||
if (!RegexPPiUtils.isPPiOK(phoneNumber)) {
|
||||
LogUtils.d(TAG, "RegexPPiUtils.isPPiOK return false.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手机号码允许
|
||||
// 中国手机号码正则表达式,以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;
|
||||
}
|
||||
|
||||
// 其他拒接
|
||||
// 检验拨不通号码群
|
||||
if (MainService.isPhoneInBoBullToon(phoneNumber)) {
|
||||
LogUtils.d(TAG, String.format("PhoneNumber %s\n Is In BoBullToon", phoneNumber));
|
||||
return false;
|
||||
}
|
||||
|
||||
public void add(String phoneRuleBlack, boolean isEnable) {
|
||||
_PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable));
|
||||
PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
|
||||
// 正则匹配规则名单校验
|
||||
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("phoneNumber :%s \nisAllowConnection %s By Rule : %s", phoneNumber, _PhoneConnectRuleModelList.get(i).isAllowConnection(), _PhoneConnectRuleModelList.get(i)));
|
||||
return _PhoneConnectRuleModelList.get(i).isAllowConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<PhoneBlackRuleBean> getPhoneBlacRuleBeanList() {
|
||||
return _PhoneBlacRuleBeanList;
|
||||
// 其他默认接收
|
||||
return true;
|
||||
}
|
||||
|
||||
public void add(String szPhoneConnectRule, boolean isAllowConnection, boolean isEnable) {
|
||||
_PhoneConnectRuleModelList.add(new PhoneConnectRuleModel(szPhoneConnectRule, isAllowConnection, isEnable));
|
||||
}
|
||||
|
||||
public ArrayList<PhoneConnectRuleModel> getPhoneBlacRuleBeanList() {
|
||||
return _PhoneConnectRuleModelList;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
9
contacts/src/main/res/drawable/recycler_view_border.xml
Normal file
9
contacts/src/main/res/drawable/recycler_view_border.xml
Normal 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>
|
||||
|
@ -90,6 +90,86 @@
|
||||
|
||||
</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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Phone Connect Rule :"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
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>
|
||||
|
28
contacts/src/main/res/layout/activity_unittest.xml
Normal file
28
contacts/src/main/res/layout/activity_unittest.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?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">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Test Main"
|
||||
android:onClick="onTestMain"/>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<cc.winboll.studio.libappbase.LogView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:id="@+id/logview"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
44
contacts/src/main/res/layout/view_phone_connect_rule.xml
Normal file
44
contacts/src/main/res/layout/view_phone_connect_rule.xml
Normal 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>
|
||||
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="编辑"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
Loading…
x
Reference in New Issue
Block a user