源码整理
This commit is contained in:
@@ -1,10 +1,5 @@
|
||||
package cc.winboll.studio.contacts;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/08/30 14:32
|
||||
* @Describe 主窗口
|
||||
*/
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
@@ -32,7 +27,6 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.contacts.activities.SettingsActivity;
|
||||
import cc.winboll.studio.contacts.beans.MainServiceBean;
|
||||
import cc.winboll.studio.contacts.fragments.CallLogFragment;
|
||||
@@ -48,43 +42,52 @@ import com.google.android.material.tabs.TabLayout;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final public class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/08/30 14:32
|
||||
* @Describe Contacts 主窗口
|
||||
*/
|
||||
public final class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
|
||||
|
||||
// ====================== 常量定义区 ======================
|
||||
public static final String TAG = "MainActivity";
|
||||
public static final int REQUEST_HOME_ACTIVITY = 0;
|
||||
public static final int REQUEST_ABOUT_ACTIVITY = 1;
|
||||
public static final int REQUEST_APP_SETTINGS = 2;
|
||||
public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoLL.ACTION_SOS";
|
||||
|
||||
static MainActivity _MainActivity;
|
||||
ADsBannerView mADsBannerView;
|
||||
|
||||
LogView mLogView;
|
||||
Toolbar mToolbar;
|
||||
CheckBox cbMainService;
|
||||
MainServiceBean mMainServiceBean;
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager viewPager;
|
||||
private List<View> views;
|
||||
ImageView[] imageViews;
|
||||
LinearLayout linearLayout;
|
||||
int currentPoint = 0;
|
||||
|
||||
private TelephonyManager telephonyManager;
|
||||
private MyPhoneStateListener phoneStateListener;
|
||||
List<Fragment> fragmentList;
|
||||
List<String> tabTitleList;
|
||||
|
||||
private static final int DIALER_REQUEST_CODE = 1;
|
||||
private static final int REQUEST_REQUIRED_PERMISSIONS = 1002;
|
||||
// 关键修改1:新增 READ_CALL_LOG 权限到必需权限列表(解决通话记录读取崩溃)
|
||||
private String[] REQUIRED_PERMISSIONS = new String[]{
|
||||
Manifest.permission.READ_CONTACTS, // 通讯录读取(原)
|
||||
Manifest.permission.CALL_PHONE, // 电话拨号(原)
|
||||
Manifest.permission.READ_CALL_LOG // 通话记录读取(新增,核心修复)
|
||||
|
||||
// ====================== 静态成员区 ======================
|
||||
static MainActivity _MainActivity;
|
||||
|
||||
// ====================== 权限常量区 ======================
|
||||
private final String[] REQUIRED_PERMISSIONS = new String[]{
|
||||
Manifest.permission.READ_CONTACTS,
|
||||
Manifest.permission.CALL_PHONE,
|
||||
Manifest.permission.READ_CALL_LOG
|
||||
};
|
||||
|
||||
// ====================== UI控件成员区 ======================
|
||||
private ADsBannerView mADsBannerView;
|
||||
private LogView mLogView;
|
||||
private Toolbar mToolbar;
|
||||
private CheckBox cbMainService;
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager viewPager;
|
||||
private List<View> views;
|
||||
private ImageView[] imageViews;
|
||||
private LinearLayout linearLayout;
|
||||
|
||||
// ====================== 业务逻辑成员区 ======================
|
||||
private MainServiceBean mMainServiceBean;
|
||||
private int currentPoint = 0;
|
||||
private TelephonyManager telephonyManager;
|
||||
private MyPhoneStateListener phoneStateListener;
|
||||
private List<Fragment> fragmentList;
|
||||
private List<String> tabTitleList;
|
||||
|
||||
// ====================== 接口实现区 ======================
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
@@ -95,105 +98,148 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
||||
return TAG;
|
||||
}
|
||||
|
||||
// ====================== 生命周期函数区 ======================
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LogUtils.d(TAG, "onCreate: 主Activity开始创建");
|
||||
_MainActivity = this;
|
||||
|
||||
// 优先检查所有必需权限(含新增的 READ_CALL_LOG)
|
||||
// 权限检查与初始化分流
|
||||
if (!checkAllRequiredPermissions()) {
|
||||
LogUtils.d(TAG, "onCreate: 检测到权限未完全授予,发起权限申请");
|
||||
requestAllRequiredPermissions();
|
||||
} else {
|
||||
LogUtils.d(TAG, "onCreate: 权限已全部授予,开始初始化UI和业务逻辑");
|
||||
initUIAndLogic(savedInstanceState);
|
||||
}
|
||||
|
||||
// 米盟广告栏
|
||||
mADsBannerView = findViewById(R.id.adsbanner);
|
||||
|
||||
//ToastUtils.show("onCreate");
|
||||
LogUtils.d(TAG, "onCreate: 主Activity创建流程结束");
|
||||
}
|
||||
|
||||
// 权限检查方法(无需修改,自动包含新增的 READ_CALL_LOG)
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
LogUtils.d(TAG, "onPostCreate: 主Activity创建完成");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
LogUtils.d(TAG, "onResume: 主Activity进入前台");
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.resumeADs(MainActivity.this);
|
||||
LogUtils.d(TAG, "onResume: 广告栏资源已恢复");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
LogUtils.d(TAG, "onDestroy: 主Activity开始销毁");
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.releaseAdResources();
|
||||
LogUtils.d(TAG, "onDestroy: 广告栏资源已释放");
|
||||
}
|
||||
LogUtils.d(TAG, "onDestroy: 主Activity销毁完成");
|
||||
}
|
||||
|
||||
// ====================== 权限相关函数区 ======================
|
||||
private boolean checkAllRequiredPermissions() {
|
||||
for (String permission : REQUIRED_PERMISSIONS) {
|
||||
if (ActivityCompat.checkSelfPermission(this, permission)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
LogUtils.w(TAG, "checkAllRequiredPermissions: 权限[" + permission + "]未授予");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 权限申请方法(无需修改,自动申请新增的 READ_CALL_LOG)
|
||||
private void requestAllRequiredPermissions() {
|
||||
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_REQUIRED_PERMISSIONS);
|
||||
}
|
||||
|
||||
// 权限结果回调(无需修改,确保所有权限(含 READ_CALL_LOG)都通过才加载UI)
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
LogUtils.d(TAG, "onRequestPermissionsResult: 权限请求结果回调,requestCode=" + requestCode);
|
||||
if (requestCode == REQUEST_REQUIRED_PERMISSIONS) {
|
||||
boolean allPermissionsGranted = true;
|
||||
for (int result : grantResults) {
|
||||
if (result != PackageManager.PERMISSION_GRANTED) {
|
||||
for (int i = 0; i < grantResults.length; i++) {
|
||||
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
||||
allPermissionsGranted = false;
|
||||
break;
|
||||
LogUtils.e(TAG, "onRequestPermissionsResult: 权限[" + permissions[i] + "]被拒绝");
|
||||
}
|
||||
}
|
||||
|
||||
if (allPermissionsGranted) {
|
||||
LogUtils.d(TAG, "onRequestPermissionsResult: 所有权限授予成功,初始化UI和逻辑");
|
||||
initUIAndLogic(null);
|
||||
} else {
|
||||
// 关键修改2:更新提示文案,告知用户新增的“通话记录权限”
|
||||
LogUtils.e(TAG, "onRequestPermissionsResult: 存在权限被拒绝,弹出提示对话框");
|
||||
showPermissionDeniedDialogAndExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 核心修改:新增“设置权限”按钮,点击调用 AppGoToSettingsUtil 跳转设置页
|
||||
private void showPermissionDeniedDialogAndExit() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("权限不足,无法使用")
|
||||
// 文案修改:明确新增“通话记录读取”权限
|
||||
.setMessage("应用需要「通讯录读取」、「电话」和「通话记录读取」权限才能正常运行,请授予权限后重新打开应用。")
|
||||
.setCancelable(false)
|
||||
// 新增:左侧“设置权限”按钮(先添加的按钮在左侧)
|
||||
.setNegativeButton("设置权限", new AlertDialog.OnClickListener() {
|
||||
.setNegativeButton("设置权限", new android.content.DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(android.content.DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
// 调用工具类跳转应用设置页(按需求实现)
|
||||
LogUtils.d(TAG, "showPermissionDeniedDialogAndExit: 用户点击设置权限,跳转应用设置页");
|
||||
AppGoToSettingsUtil appGoToSettingsUtil = new AppGoToSettingsUtil();
|
||||
appGoToSettingsUtil.GoToSetting(MainActivity.this);
|
||||
}
|
||||
})
|
||||
// 原有:右侧“确定退出”按钮(后添加的按钮在右侧)
|
||||
.setPositiveButton("确定退出", new AlertDialog.OnClickListener() {
|
||||
.setPositiveButton("确定退出", new android.content.DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(android.content.DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
LogUtils.d(TAG, "showPermissionDeniedDialogAndExit: 用户点击退出,关闭应用");
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
// 初始化UI和逻辑(无需修改,权限通过后才加载 CallLogFragment)
|
||||
// ====================== UI与逻辑初始化区 ======================
|
||||
private void initUIAndLogic(Bundle savedInstanceState) {
|
||||
LogUtils.d(TAG, "initUIAndLogic: 开始初始化UI布局");
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
// 工具栏初始化
|
||||
mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1);
|
||||
setSupportActionBar(mToolbar);
|
||||
getSupportActionBar().setSubtitle(TAG);
|
||||
|
||||
// Tab与ViewPager初始化
|
||||
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
|
||||
viewPager = (ViewPager) findViewById(R.id.viewPager);
|
||||
initViewPagerAndTabs();
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
LogUtils.d(TAG, "initUIAndLogic: ViewPager与TabLayout初始化完成");
|
||||
|
||||
// 广告栏初始化
|
||||
mADsBannerView = (ADsBannerView) findViewById(R.id.adsbanner);
|
||||
LogUtils.d(TAG, "initUIAndLogic: 广告栏控件初始化完成");
|
||||
|
||||
// 服务启动逻辑
|
||||
initMainService();
|
||||
LogUtils.d(TAG, "initUIAndLogic: 主服务初始化完成");
|
||||
|
||||
// 电话状态监听初始化
|
||||
initPhoneStateListener();
|
||||
LogUtils.d(TAG, "initUIAndLogic: 电话状态监听器初始化完成");
|
||||
}
|
||||
|
||||
private void initViewPagerAndTabs() {
|
||||
fragmentList = new ArrayList<Fragment>();
|
||||
tabTitleList = new ArrayList<String>();
|
||||
// CallLogFragment 仅在权限通过后才实例化(避免提前触发读取)
|
||||
|
||||
fragmentList.add(CallLogFragment.newInstance(0));
|
||||
fragmentList.add(ContactsFragment.newInstance(1));
|
||||
fragmentList.add(LogFragment.newInstance(2));
|
||||
@@ -203,39 +249,140 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
||||
|
||||
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), fragmentList, tabTitleList);
|
||||
viewPager.setAdapter(adapter);
|
||||
viewPager.setOffscreenPageLimit(0); // 关闭预加载,避免提前初始化 CallLogFragment
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
viewPager.setOffscreenPageLimit(0);
|
||||
viewPager.addOnPageChangeListener(this);
|
||||
}
|
||||
|
||||
// 原有服务启动、电话监听等逻辑...
|
||||
MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
private void initMainService() {
|
||||
mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
|
||||
if (mMainServiceBean == null) {
|
||||
LogUtils.d(TAG, "initMainService: 服务配置Bean不存在,创建新实例并保存");
|
||||
mMainServiceBean = new MainServiceBean();
|
||||
MainServiceBean.saveBean(this, mMainServiceBean);
|
||||
}
|
||||
|
||||
if (mMainServiceBean.isEnable()) {
|
||||
LogUtils.d(TAG, "initMainService: 主服务已启用,延迟1秒启动服务");
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MainService.startMainService(MainActivity.this);
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
LogUtils.d(TAG, "initMainService: 主服务未启用,跳过启动流程");
|
||||
}
|
||||
}
|
||||
|
||||
private void initPhoneStateListener() {
|
||||
telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
|
||||
phoneStateListener = new MyPhoneStateListener();
|
||||
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||
}
|
||||
|
||||
// ====================== 菜单相关函数区 ======================
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
// 以下为原有代码(无需修改)
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.item_settings) {
|
||||
LogUtils.d(TAG, "onOptionsItemSelected: 用户点击设置菜单,跳转设置页面");
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
// ====================== 页面回调函数区 ======================
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
currentPoint = position;
|
||||
LogUtils.d(TAG, "onPageSelected: 页面切换至索引" + position + ",标题=" + tabTitleList.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {}
|
||||
|
||||
// ====================== 电话相关工具函数区 ======================
|
||||
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) {
|
||||
LogUtils.e(TAG, "dialPhoneNumber: 拨号权限不足,无法发起拨号");
|
||||
Toast.makeText(_MainActivity, "拨号权限不足", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
LogUtils.d(TAG, "dialPhoneNumber: 发起拨号,号码=" + phoneNumber);
|
||||
_MainActivity.startActivity(intent);
|
||||
}
|
||||
|
||||
public boolean isDefaultPhoneCallApp() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
TelecomManager manager = (TelecomManager) getSystemService(TELECOM_SERVICE);
|
||||
if (manager != null && manager.getDefaultDialerPackage() != null) {
|
||||
boolean isDefault = manager.getDefaultDialerPackage().equals(getPackageName());
|
||||
LogUtils.d(TAG, "isDefaultPhoneCallApp: 当前应用是否为默认拨号应用=" + isDefault);
|
||||
return isDefault;
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "isDefaultPhoneCallApp: 系统版本低于M,无法判断默认拨号应用");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ====================== 服务状态工具函数区 ======================
|
||||
public boolean isServiceRunning(Class<?> serviceClass) {
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (manager == null) {
|
||||
LogUtils.w(TAG, "isServiceRunning: ActivityManager获取失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
if (serviceClass.getName().equals(service.service.getClassName())) {
|
||||
LogUtils.d(TAG, "isServiceRunning: 服务[" + serviceClass.getName() + "]正在运行");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "isServiceRunning: 服务[" + serviceClass.getName() + "]未运行");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ====================== 活动结果回调区 ======================
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
LogUtils.d(TAG, "onActivityResult: 回调触发,requestCode=" + requestCode + ",resultCode=" + resultCode);
|
||||
if (requestCode == DIALER_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
LogUtils.d(TAG, "onActivityResult: 设为默认拨号应用成功");
|
||||
Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else if (requestCode == REQUEST_APP_SETTINGS) {
|
||||
LogUtils.d(TAG, "onActivityResult: 从设置页返回,重建Activity");
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 内部类定义区 ======================
|
||||
private class MyPagerAdapter extends FragmentPagerAdapter {
|
||||
private List<Fragment> fragmentList;
|
||||
private List<String> tabTitleList;
|
||||
private final List<Fragment> fragmentList;
|
||||
private final List<String> tabTitleList;
|
||||
|
||||
public MyPagerAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> tabTitleList) {
|
||||
super(fm);
|
||||
this.fragmentList = fragmentList;
|
||||
this.tabTitleList = tabTitleList;
|
||||
LogUtils.d(TAG, "MyPagerAdapter: ViewPager适配器初始化,Fragment数量=" + fragmentList.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -254,113 +401,21 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Toast.makeText(_MainActivity, "拨号权限不足", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
_MainActivity.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {}
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
|
||||
@Override
|
||||
public void onPageSelected(int position) {}
|
||||
@Override
|
||||
public void onClick(View v) {}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
private class MyPhoneStateListener extends PhoneStateListener {
|
||||
@Override
|
||||
public void onCallStateChanged(int state, String incomingNumber) {
|
||||
switch (state) {
|
||||
case TelephonyManager.CALL_STATE_IDLE:
|
||||
LogUtils.d(TAG, "电话已挂断");
|
||||
LogUtils.d(TAG, "onCallStateChanged: 电话状态-空闲");
|
||||
break;
|
||||
case TelephonyManager.CALL_STATE_OFFHOOK:
|
||||
LogUtils.d(TAG, "正在通话中");
|
||||
LogUtils.d(TAG, "onCallStateChanged: 电话状态-通话中");
|
||||
break;
|
||||
case TelephonyManager.CALL_STATE_RINGING:
|
||||
LogUtils.d(TAG, "来电: " + incomingNumber);
|
||||
LogUtils.d(TAG, "onCallStateChanged: 电话状态-来电,号码=" + incomingNumber);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.releaseAdResources();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.item_settings) {
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.resumeADs(MainActivity.this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDefaultPhoneCallApp() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
|
||||
if (manger != null && manger.getDefaultDialerPackage() != null) {
|
||||
return manger.getDefaultDialerPackage().equals(getPackageName());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isServiceRunning(Class<?> serviceClass) {
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (manager == null) return false;
|
||||
|
||||
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
|
||||
Integer.MAX_VALUE)) {
|
||||
if (serviceClass.getName().equals(service.service.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == DIALER_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else if (requestCode == REQUEST_APP_SETTINGS) {
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package cc.winboll.studio.contacts.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/02/21 05:37:42
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
@@ -24,7 +20,6 @@ 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.App;
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.contacts.adapters.PhoneConnectRuleAdapter;
|
||||
import cc.winboll.studio.contacts.beans.MainServiceBean;
|
||||
@@ -42,32 +37,40 @@ import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/02/21 05:37:42
|
||||
* @Describe Contacts 主窗口
|
||||
*/
|
||||
public class SettingsActivity extends AppCompatActivity implements IWinBoLLActivity {
|
||||
|
||||
// ====================== 常量定义区 ======================
|
||||
public static final String TAG = "SettingsActivity";
|
||||
|
||||
Toolbar mToolbar;
|
||||
Switch swSilent;
|
||||
SeekBar msbVolume;
|
||||
TextView mtvVolume;
|
||||
int mnStreamMaxVolume;
|
||||
int mnStreamVolume;
|
||||
Switch mswMainService;
|
||||
// ====================== 静态控件区 ======================
|
||||
static DuInfoTextView _DuInfoTextView;
|
||||
|
||||
// 云盾防御层数量
|
||||
EditText etDunTotalCount;
|
||||
// 防御层恢复时间间隔(秒钟)
|
||||
EditText etDunResumeSecondCount;
|
||||
// 每次恢复防御层数
|
||||
EditText etDunResumeCount;
|
||||
// 是否启用云盾
|
||||
Switch swIsEnableDun;
|
||||
|
||||
// ====================== UI控件区 ======================
|
||||
private Toolbar mToolbar;
|
||||
private Switch swSilent;
|
||||
private SeekBar msbVolume;
|
||||
private TextView mtvVolume;
|
||||
private Switch mswMainService;
|
||||
private EditText etDunTotalCount;
|
||||
private EditText etDunResumeSecondCount;
|
||||
private EditText etDunResumeCount;
|
||||
private Switch swIsEnableDun;
|
||||
private RecyclerView recyclerView;
|
||||
private EditText etBoBullToonURL;
|
||||
private EditText etPhone;
|
||||
|
||||
// ====================== 数据与业务参数区 ======================
|
||||
private int mnStreamMaxVolume;
|
||||
private int mnStreamVolume;
|
||||
private PhoneConnectRuleAdapter adapter;
|
||||
private List<PhoneConnectRuleModel> ruleList;
|
||||
|
||||
// ====================== 接口实现区 ======================
|
||||
@Override
|
||||
public AppCompatActivity getActivity() {
|
||||
return this;
|
||||
@@ -78,102 +81,132 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
|
||||
return TAG;
|
||||
}
|
||||
|
||||
// ====================== 生命周期函数区 ======================
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LogUtils.d(TAG, "onCreate: 设置页面开始创建");
|
||||
setContentView(R.layout.activity_settings);
|
||||
|
||||
// 初始化工具栏
|
||||
mToolbar = findViewById(R.id.activitymainToolbar1);
|
||||
initToolbar();
|
||||
// 初始化主服务开关
|
||||
initMainServiceSwitch();
|
||||
// 初始化铃声音量调节
|
||||
initVolumeSeekBar();
|
||||
// 初始化规则列表
|
||||
initRuleRecyclerView();
|
||||
// 初始化云盾设置
|
||||
initDunSettings();
|
||||
// 初始化BoBullToon相关控件
|
||||
initBoBullToonViews();
|
||||
|
||||
LogUtils.d(TAG, "onCreate: 设置页面初始化完成");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
LogUtils.d(TAG, "onDestroy: 设置页面开始销毁");
|
||||
_DuInfoTextView = null; // 清空静态引用,避免内存泄漏
|
||||
LogUtils.d(TAG, "onDestroy: 设置页面销毁完成");
|
||||
}
|
||||
|
||||
// ====================== 控件初始化函数区 ======================
|
||||
private void initToolbar() {
|
||||
LogUtils.d(TAG, "initToolbar: 初始化工具栏");
|
||||
mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1);
|
||||
setSupportActionBar(mToolbar);
|
||||
// 显示后退按钮
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setSubtitle(getTag());
|
||||
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setSubtitle(getTag());
|
||||
}
|
||||
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "【导航栏】点击返回");
|
||||
LogUtils.d(TAG, "initToolbar: 点击导航栏返回按钮");
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mswMainService = findViewById(R.id.sw_mainservice);
|
||||
private void initMainServiceSwitch() {
|
||||
LogUtils.d(TAG, "initMainServiceSwitch: 初始化主服务开关");
|
||||
mswMainService = (Switch) 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
mswMainService.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
LogUtils.d(TAG, "initMainServiceSwitch: 主服务开关状态变更,checked=" + mswMainService.isChecked());
|
||||
if (mswMainService.isChecked()) {
|
||||
MainService.startMainServiceAndSaveStatus(SettingsActivity.this);
|
||||
} else {
|
||||
MainService.stopMainServiceAndSaveStatus(SettingsActivity.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
msbVolume = findViewById(R.id.bellvolume);
|
||||
mtvVolume = findViewById(R.id.tv_volume);
|
||||
private void initVolumeSeekBar() {
|
||||
LogUtils.d(TAG, "initVolumeSeekBar: 初始化音量调节条");
|
||||
msbVolume = (SeekBar) findViewById(R.id.bellvolume);
|
||||
mtvVolume = (TextView) findViewById(R.id.tv_volume);
|
||||
final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
|
||||
// 设置SeekBar的最大值为系统铃声音量的最大刻度
|
||||
// 设置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 onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (fromUser) {
|
||||
LogUtils.d(TAG, "initVolumeSeekBar: 音量调节至" + progress);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// 当开始拖动SeekBar时的操作
|
||||
}
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// 当停止拖动SeekBar时的操作
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
recyclerView = findViewById(R.id.recycler_view);
|
||||
private void initRuleRecyclerView() {
|
||||
LogUtils.d(TAG, "initRuleRecyclerView: 初始化规则列表");
|
||||
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
ruleList = Rules.getInstance(this).getPhoneBlacRuleBeanList();
|
||||
|
||||
adapter = new PhoneConnectRuleAdapter(this, ruleList);
|
||||
recyclerView.setAdapter(adapter);
|
||||
LogUtils.d(TAG, "initRuleRecyclerView: 规则列表加载完成,共" + ruleList.size() + "条规则");
|
||||
}
|
||||
|
||||
private void initDunSettings() {
|
||||
LogUtils.d(TAG, "initDunSettings: 初始化云盾设置");
|
||||
_DuInfoTextView = (DuInfoTextView) findViewById(R.id.tv_DunInfo);
|
||||
etDunTotalCount = (EditText) findViewById(R.id.et_DunTotalCount);
|
||||
etDunResumeSecondCount = (EditText) findViewById(R.id.et_DunResumeSecondCount);
|
||||
etDunResumeCount = (EditText) findViewById(R.id.et_DunResumeCount);
|
||||
swIsEnableDun = (Switch) findViewById(R.id.sw_IsEnableDun);
|
||||
|
||||
// 设置参数云盾
|
||||
_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()));
|
||||
@@ -183,19 +216,33 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
|
||||
etDunTotalCount.setEnabled(!isEnableDun);
|
||||
etDunResumeSecondCount.setEnabled(!isEnableDun);
|
||||
etDunResumeCount.setEnabled(!isEnableDun);
|
||||
|
||||
EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
|
||||
etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
|
||||
}
|
||||
|
||||
private void initBoBullToonViews() {
|
||||
LogUtils.d(TAG, "initBoBullToonViews: 初始化BoBullToon相关控件");
|
||||
etBoBullToonURL = (EditText) findViewById(R.id.bobulltoonurl_et);
|
||||
etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
|
||||
etPhone = (EditText) findViewById(R.id.activitysettingsEditText1);
|
||||
}
|
||||
|
||||
// ====================== 工具函数区 ======================
|
||||
private void updateStreamVolumeTextView() {
|
||||
mtvVolume.setText(String.format("%d/%d", mnStreamVolume, mnStreamMaxVolume));
|
||||
}
|
||||
|
||||
// ====================== 静态通知函数区 ======================
|
||||
public static void notifyDunInfoUpdate() {
|
||||
if (_DuInfoTextView != null) {
|
||||
LogUtils.d(TAG, "notifyDunInfoUpdate: 更新云盾信息展示");
|
||||
_DuInfoTextView.notifyInfoUpdate();
|
||||
} else {
|
||||
LogUtils.w(TAG, "notifyDunInfoUpdate: 云盾信息控件为空,跳过更新");
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 点击事件回调函数区 ======================
|
||||
public void onSW_IsEnableDun(View view) {
|
||||
LogUtils.d(TAG, "onSW_IsEnableDun");
|
||||
LogUtils.d(TAG, "onSW_IsEnableDun: 云盾开关状态变更,checked=" + swIsEnableDun.isChecked());
|
||||
boolean isEnableDun = swIsEnableDun.isChecked();
|
||||
etDunTotalCount.setEnabled(!isEnableDun);
|
||||
etDunResumeSecondCount.setEnabled(!isEnableDun);
|
||||
@@ -203,154 +250,176 @@ public class SettingsActivity extends AppCompatActivity implements IWinBoLLActiv
|
||||
|
||||
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()));
|
||||
|
||||
// 应用效果提示
|
||||
ToastUtils.show((settingsModel.getDunTotalCount() == 1)?"电话骚扰防御力几乎为0。":String.format("以下设置将在连拨%d次后接通电话。", settingsModel.getDunTotalCount()));
|
||||
try {
|
||||
int totalCount = Integer.parseInt(etDunTotalCount.getText().toString());
|
||||
int resumeSecond = Integer.parseInt(etDunResumeSecondCount.getText().toString());
|
||||
int resumeCount = Integer.parseInt(etDunResumeCount.getText().toString());
|
||||
settingsModel.setDunTotalCount(totalCount);
|
||||
settingsModel.setDunResumeSecondCount(resumeSecond);
|
||||
settingsModel.setDunResumeCount(resumeCount);
|
||||
ToastUtils.show((totalCount == 1) ? "电话骚扰防御力几乎为0。" : String.format("以下设置将在连拨%d次后接通电话。", totalCount));
|
||||
LogUtils.d(TAG, "onSW_IsEnableDun: 云盾参数更新完成,totalCount=" + totalCount);
|
||||
} catch (NumberFormatException e) {
|
||||
LogUtils.e(TAG, "onSW_IsEnableDun: 云盾参数格式错误", e);
|
||||
ToastUtils.show("参数格式错误,请输入整数");
|
||||
swIsEnableDun.setChecked(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
settingsModel.setIsEnableDun(isEnableDun);
|
||||
Rules.getInstance(this).saveDun();
|
||||
Rules.getInstance(this).reload();
|
||||
|
||||
// 重新加载盾牌参数
|
||||
|
||||
// 刷新参数显示
|
||||
etDunTotalCount.setText(Integer.toString(settingsModel.getDunTotalCount()));
|
||||
etDunResumeSecondCount.setText(Integer.toString(settingsModel.getDunResumeSecondCount()));
|
||||
etDunResumeCount.setText(Integer.toString(settingsModel.getDunResumeCount()));
|
||||
|
||||
}
|
||||
|
||||
void updateStreamVolumeTextView() {
|
||||
mtvVolume.setText(String.format("%d/%d", mnStreamVolume, mnStreamMaxVolume));
|
||||
}
|
||||
|
||||
public void onUnitTest(View view) {
|
||||
LogUtils.d(TAG, "onUnitTest: 点击进入单元测试页面");
|
||||
Intent intent = new Intent(this, UnitTestActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void onAddNewConnectionRule(View view) {
|
||||
LogUtils.d(TAG, "onAddNewConnectionRule: 添加新的连接规则");
|
||||
Rules.getInstance(this).getPhoneBlacRuleBeanList().add(new PhoneConnectRuleModel());
|
||||
Rules.getInstance(this).saveRules();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void onDefaultPhone(View view) {
|
||||
LogUtils.d(TAG, "onDefaultPhone: 跳转到默认电话应用设置");
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void onCanDrawOverlays(View view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !Settings.canDrawOverlays(this)) {
|
||||
// 请求 悬浮框 权限
|
||||
LogUtils.d(TAG, "onCanDrawOverlays: 检查悬浮窗权限");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
|
||||
askForDrawOverlay();
|
||||
} else {
|
||||
ToastUtils.show("悬浮窗已开启");
|
||||
}
|
||||
}
|
||||
|
||||
public void onCleanBoBullToonData(View view) {
|
||||
|
||||
public void onCleanBoBullToonData(View view) {
|
||||
LogUtils.d(TAG, "onCleanBoBullToonData: 清理BoBullToon数据");
|
||||
TomCat tomCat = TomCat.getInstance(this);
|
||||
tomCat.cleanBoBullToon();
|
||||
tomCat.cleanBoBullToon();
|
||||
ToastUtils.show("BoBullToon数据已清理");
|
||||
}
|
||||
|
||||
public void onResetBoBullToonURL(View view) {
|
||||
LogUtils.d(TAG, "onResetBoBullToonURL: 重置BoBullToon URL");
|
||||
Rules.getInstance(this).resetDefaultBoBullToonURL();
|
||||
EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
|
||||
etBoBullToonURL.setText(Rules.getInstance(this).getBoBullToonURL());
|
||||
ToastUtils.show("已重置 BoBullToon URL。");
|
||||
ToastUtils.show("已重置 BoBullToon URL。");
|
||||
}
|
||||
|
||||
public void onDownloadBoBullToon(View view) {
|
||||
EditText etBoBullToonURL = findViewById(R.id.bobulltoonurl_et);
|
||||
if (!etBoBullToonURL.getText().toString().trim().equals(Rules.getInstance(this).getBoBullToonURL())) {
|
||||
Rules.getInstance(this).setBoBullToonURL(etBoBullToonURL.getText().toString().trim());
|
||||
LogUtils.d(TAG, "onDownloadBoBullToon: 开始下载BoBullToon数据");
|
||||
String inputUrl = etBoBullToonURL.getText().toString().trim();
|
||||
String savedUrl = Rules.getInstance(this).getBoBullToonURL();
|
||||
if (!inputUrl.equals(savedUrl)) {
|
||||
Rules.getInstance(this).setBoBullToonURL(inputUrl);
|
||||
LogUtils.d(TAG, "onDownloadBoBullToon: BoBullToon URL已更新为" + inputUrl);
|
||||
}
|
||||
|
||||
final TomCat tomCat = TomCat.getInstance(this);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (tomCat.downloadBoBullToon()) {
|
||||
ToastUtils.show("BoBullToon downlaod OK!");
|
||||
LogUtils.d(TAG, "BoBullToon downlaod OK!");
|
||||
MainService.restartMainService(SettingsActivity.this);
|
||||
Rules.getInstance(SettingsActivity.this).reload();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
@Override
|
||||
public void run() {
|
||||
if (tomCat.downloadBoBullToon()) {
|
||||
LogUtils.d(TAG, "onDownloadBoBullToon: BoBullToon 下载成功");
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ToastUtils.show("BoBullToon download OK!");
|
||||
}
|
||||
});
|
||||
MainService.restartMainService(SettingsActivity.this);
|
||||
Rules.getInstance(SettingsActivity.this).reload();
|
||||
} else {
|
||||
LogUtils.e(TAG, "onDownloadBoBullToon: BoBullToon 下载失败");
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void onSearchBoBullToonPhone(View view) {
|
||||
LogUtils.d(TAG, "onSearchBoBullToonPhone: 搜索BoBullToon号码");
|
||||
TomCat tomCat = TomCat.getInstance(this);
|
||||
EditText etPhone = findViewById(R.id.activitysettingsEditText1);
|
||||
String phone = etPhone.getText().toString().trim();
|
||||
if (phone.isEmpty()) {
|
||||
ToastUtils.show("请输入要查询的号码");
|
||||
return;
|
||||
}
|
||||
if (tomCat.loadPhoneBoBullToon()) {
|
||||
if (tomCat.isPhoneBoBullToon(phone)) {
|
||||
ToastUtils.show("It is a BoBullToon Phone!");
|
||||
} else {
|
||||
ToastUtils.show("Not in BoBullToon.");
|
||||
}
|
||||
boolean isBoBullToon = tomCat.isPhoneBoBullToon(phone);
|
||||
ToastUtils.show(isBoBullToon ? "It is a BoBullToon Phone!" : "Not in BoBullToon.");
|
||||
LogUtils.d(TAG, "onSearchBoBullToonPhone: 号码" + phone + "查询结果:" + (isBoBullToon ? "是" : "否") + "为BoBullToon号码");
|
||||
} else {
|
||||
ToastUtils.show("没有下载 BoBullToon。");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void askForDrawOverlay() {
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(this)
|
||||
.setTitle("允许显示悬浮框")
|
||||
.setMessage("为了使电话监听服务正常工作,请允许这项权限")
|
||||
.setPositiveButton("去设置", new DialogInterface.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
openDrawOverlaySettings();
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("稍后再说", new DialogInterface.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
public void onAbout(View view) {
|
||||
LogUtils.d(TAG, "onAbout: 跳转到关于页面");
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, AboutActivity.class);
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
alertDialog.getWindow().setFlags(
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||
public void onLogView(View view) {
|
||||
LogUtils.d(TAG, "onLogView: 跳转到日志页面");
|
||||
WinBoLLActivityManager.getInstance().startLogActivity(this);
|
||||
}
|
||||
|
||||
// ====================== 悬浮窗权限请求函数区 ======================
|
||||
private void askForDrawOverlay() {
|
||||
LogUtils.d(TAG, "askForDrawOverlay: 弹出悬浮窗权限请求对话框");
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(this)
|
||||
.setTitle("允许显示悬浮框")
|
||||
.setMessage("为了使电话监听服务正常工作,请允许这项权限")
|
||||
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
openDrawOverlaySettings();
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("稍后再说", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
|
||||
if (alertDialog.getWindow() != null) {
|
||||
alertDialog.getWindow().setFlags(
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||
}
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转悬浮窗管理设置界面
|
||||
*/
|
||||
private void openDrawOverlaySettings() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// Android M 以上引导用户去系统设置中打开允许悬浮窗
|
||||
// 使用反射是为了用尽可能少的代码保证在大部分机型上都可用
|
||||
LogUtils.d(TAG, "openDrawOverlaySettings: 跳转悬浮窗管理设置界面");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
try {
|
||||
Context context = this;
|
||||
Class clazz = Settings.class;
|
||||
Class<?> clazz = Settings.class;
|
||||
Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
|
||||
Intent intent = new Intent(field.get(null).toString());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setData(Uri.parse("package:" + context.getPackageName()));
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "openDrawOverlaySettings: 跳转悬浮窗设置失败", e);
|
||||
Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onAbout(View view) {
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, AboutActivity.class);
|
||||
}
|
||||
|
||||
public void onLogView(View view) {
|
||||
WinBoLLActivityManager.getInstance().startLogActivity(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package cc.winboll.studio.contacts.fragments;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/02/20 12:57:00
|
||||
* @Describe 拨号
|
||||
*/
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
@@ -25,41 +20,45 @@ 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 cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/02/20 12:57:00
|
||||
* @Describe 通话记录区域视图
|
||||
*/
|
||||
public class CallLogFragment extends Fragment {
|
||||
|
||||
public static final String TAG = "CallFragment";
|
||||
// ====================== 常量定义区 ======================
|
||||
public static final String TAG = "CallLogFragment";
|
||||
public static final int MSG_UPDATE = 1;
|
||||
private static final String ARG_PAGE = "ARG_PAGE";
|
||||
private static final int REQUEST_READ_CALL_LOG = 1;
|
||||
|
||||
// ====================== 静态成员区 ======================
|
||||
static volatile CallLogFragment _CallLogFragment;
|
||||
|
||||
public static final int MSG_UPDATE = 1; // 添加消息常量
|
||||
|
||||
private static final String ARG_PAGE = "ARG_PAGE";
|
||||
// ====================== 页面参数区 ======================
|
||||
private int mPage;
|
||||
|
||||
private static final int REQUEST_READ_CALL_LOG = 1;
|
||||
// ====================== UI控件与适配器区 ======================
|
||||
private RecyclerView recyclerView;
|
||||
private CallLogAdapter callLogAdapter;
|
||||
private List<CallLogModel> callLogList = new ArrayList<>();
|
||||
private List<CallLogModel> callLogList = new ArrayList<CallLogModel>();
|
||||
|
||||
// 添加Handler
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(@NonNull Message msg) {
|
||||
if (msg.what == MSG_UPDATE) {
|
||||
readCallLog(); // 接收到消息时更新通话记录
|
||||
}
|
||||
}
|
||||
};
|
||||
// ====================== 业务逻辑成员区 ======================
|
||||
private Handler mHandler;
|
||||
|
||||
// ====================== 单例与实例化函数区 ======================
|
||||
CallLogFragment() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static CallLogFragment newInstance(int page) {
|
||||
LogUtils.d(TAG, "newInstance: 创建通话记录Fragment实例,页码=" + page);
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_PAGE, page);
|
||||
CallLogFragment fragment = new CallLogFragment();
|
||||
@@ -68,67 +67,131 @@ public class CallLogFragment extends Fragment {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_call_log, container, false);
|
||||
}
|
||||
|
||||
// ====================== 生命周期函数区 ======================
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LogUtils.d(TAG, "onCreate: Fragment创建开始");
|
||||
if (getArguments() != null) {
|
||||
mPage = getArguments().getInt(ARG_PAGE);
|
||||
LogUtils.d(TAG, "onCreate: 读取页面参数,mPage=" + mPage);
|
||||
}
|
||||
// Java7 兼容:移除Lambda,使用匿名内部类初始化Handler
|
||||
mHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg.what == MSG_UPDATE) {
|
||||
LogUtils.d(TAG, "handleMessage: 收到更新消息,开始读取通话记录");
|
||||
readCallLog();
|
||||
}
|
||||
}
|
||||
};
|
||||
LogUtils.d(TAG, "onCreate: Fragment创建完成");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
LogUtils.d(TAG, "onCreateView: 加载Fragment布局");
|
||||
return inflater.inflate(R.layout.fragment_call_log, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
recyclerView = view.findViewById(R.id.recyclerView);
|
||||
LogUtils.d(TAG, "onViewCreated: 视图创建完成,初始化控件");
|
||||
// 初始化RecyclerView
|
||||
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
callLogAdapter = new CallLogAdapter(getContext(), callLogList);
|
||||
recyclerView.setAdapter(callLogAdapter);
|
||||
LogUtils.d(TAG, "onViewCreated: RecyclerView初始化完成");
|
||||
|
||||
// 权限检查与数据加载
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
|
||||
LogUtils.w(TAG, "onViewCreated: 读取通话记录权限未授予,发起权限申请");
|
||||
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CALL_LOG}, REQUEST_READ_CALL_LOG);
|
||||
} else {
|
||||
mHandler.sendEmptyMessage(MSG_UPDATE); // 通过Handler触发更新
|
||||
LogUtils.d(TAG, "onViewCreated: 权限已授予,发送更新消息");
|
||||
mHandler.sendEmptyMessage(MSG_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
LogUtils.d(TAG, "onResume: Fragment进入前台,刷新数据");
|
||||
if (callLogAdapter != null) {
|
||||
callLogAdapter.relaodContacts();
|
||||
LogUtils.d(TAG, "onResume: 适配器数据重新加载");
|
||||
}
|
||||
readCallLog();
|
||||
LogUtils.d(TAG, "onResume: 通话记录数据刷新完成");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
LogUtils.d(TAG, "onDestroy: Fragment开始销毁");
|
||||
if (mHandler != null) {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
LogUtils.d(TAG, "onDestroy: Handler消息已清空");
|
||||
}
|
||||
_CallLogFragment = null;
|
||||
LogUtils.d(TAG, "onDestroy: Fragment销毁完成");
|
||||
}
|
||||
|
||||
// ====================== 权限回调函数区 ======================
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
LogUtils.d(TAG, "onRequestPermissionsResult: 权限请求回调,requestCode=" + requestCode);
|
||||
if (requestCode == REQUEST_READ_CALL_LOG) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
mHandler.sendEmptyMessage(MSG_UPDATE); // 通过Handler触发更新
|
||||
LogUtils.d(TAG, "onRequestPermissionsResult: 通话记录权限授予成功,发送更新消息");
|
||||
mHandler.sendEmptyMessage(MSG_UPDATE);
|
||||
} else {
|
||||
LogUtils.e(TAG, "onRequestPermissionsResult: 通话记录权限被拒绝");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 业务核心函数区 ======================
|
||||
private void readCallLog() {
|
||||
callLogList.clear(); // 清空原有数据
|
||||
Cursor cursor = requireContext().getContentResolver().query(
|
||||
CallLog.Calls.CONTENT_URI,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CallLog.Calls.DATE + " DESC");
|
||||
LogUtils.d(TAG, "readCallLog: 开始读取系统通话记录");
|
||||
callLogList.clear();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = requireContext().getContentResolver().query(
|
||||
CallLog.Calls.CONTENT_URI,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CallLog.Calls.DATE + " DESC"
|
||||
);
|
||||
if (cursor != null) {
|
||||
LogUtils.d(TAG, "readCallLog: 成功获取通话记录游标,数据条数=" + cursor.getCount());
|
||||
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);
|
||||
|
||||
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));
|
||||
callLogList.add(new CallLogModel(phoneNumber, callStatus, callDate));
|
||||
}
|
||||
callLogAdapter.notifyDataSetChanged();
|
||||
LogUtils.d(TAG, "readCallLog: 通话记录数据解析完成,共" + callLogList.size() + "条");
|
||||
} else {
|
||||
LogUtils.w(TAG, "readCallLog: 通话记录游标为空");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "readCallLog: 读取通话记录异常", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
LogUtils.d(TAG, "readCallLog: 游标已关闭");
|
||||
}
|
||||
cursor.close();
|
||||
callLogAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,27 +208,19 @@ public class CallLogFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mHandler.removeCallbacksAndMessages(null); // 清理Handler防止内存泄漏
|
||||
}
|
||||
|
||||
// ====================== 外部调用函数区 ======================
|
||||
public void triggerUpdate() {
|
||||
LogUtils.d(TAG, "triggerUpdate: 外部触发通话记录更新");
|
||||
mHandler.sendEmptyMessage(MSG_UPDATE);
|
||||
}
|
||||
|
||||
public static void updateCallLogFragment() {
|
||||
if (_CallLogFragment != null) {
|
||||
LogUtils.d(TAG, "updateCallLogFragment: 静态方法触发Fragment更新");
|
||||
_CallLogFragment.triggerUpdate();
|
||||
} else {
|
||||
LogUtils.w(TAG, "updateCallLogFragment: Fragment实例为空,无法更新");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
//ToastUtils.show("onResume");
|
||||
callLogAdapter.relaodContacts();
|
||||
readCallLog(); // 窗口回显时更新通话记录
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package cc.winboll.studio.contacts.fragments;
|
||||
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/08/30 14:32
|
||||
* @Describe 联系人视图
|
||||
*/
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -21,50 +15,65 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
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.ContactAdapter;
|
||||
import cc.winboll.studio.contacts.beans.ContactModel;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.contacts.adapters.ContactAdapter;
|
||||
import cc.winboll.studio.contacts.beans.ContactModel;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/08/30 14:32
|
||||
* @Describe 联系人区域视图
|
||||
*/
|
||||
public class ContactsFragment extends Fragment {
|
||||
|
||||
// ====================== 常量定义区 ======================
|
||||
public static final String TAG = "ContactsFragment";
|
||||
private static final String ARG_PAGE = "ARG_PAGE";
|
||||
private static final int REQUEST_READ_CONTACTS = 1;
|
||||
private static final long DEBOUNCE_DELAY = 300; // 搜索防抖延迟
|
||||
|
||||
// ====================== 静态缓存区 ======================
|
||||
// 全局复用联系人数据,减少重复查询
|
||||
private static List<ContactModel> sCachedOriginalList = new ArrayList<ContactModel>();
|
||||
private static List<ContactModel> sCachedFilteredList = new ArrayList<ContactModel>();
|
||||
|
||||
// ====================== 页面参数区 ======================
|
||||
private int mPage;
|
||||
private boolean isViewInitialized = false; // 视图初始化标记
|
||||
private boolean isDataLoaded = false; // 数据加载标记
|
||||
|
||||
// ====================== UI控件区 ======================
|
||||
private RecyclerView recyclerView;
|
||||
private ContactAdapter contactAdapter;
|
||||
private EditText searchEditText;
|
||||
private Button btnDial;
|
||||
private boolean isViewInitialized = false; // 标记视图是否已初始化
|
||||
|
||||
// 静态缓存:全局复用联系人数据
|
||||
private static List<ContactModel> sCachedOriginalList = new ArrayList<ContactModel>();
|
||||
private static List<ContactModel> sCachedFilteredList = new ArrayList<ContactModel>();
|
||||
|
||||
// 当前页面数据容器
|
||||
// ====================== 数据容器区 ======================
|
||||
private List<ContactModel> contactList = new ArrayList<ContactModel>();
|
||||
private List<ContactModel> originalContactList = new ArrayList<ContactModel>();
|
||||
|
||||
// 异步工具
|
||||
// ====================== 异步工具区 ======================
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private boolean isDataLoaded = false;
|
||||
|
||||
|
||||
// ====================== 实例化函数区 ======================
|
||||
public static ContactsFragment newInstance(int page) {
|
||||
LogUtils.d(TAG, "newInstance: 创建联系人Fragment实例,页码=" + page);
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_PAGE, page);
|
||||
ContactsFragment fragment = new ContactsFragment();
|
||||
@@ -72,61 +81,115 @@ public class ContactsFragment extends Fragment {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
// ====================== 生命周期函数区 ======================
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LogUtils.d(TAG, "onCreate: Fragment创建开始");
|
||||
if (getArguments() != null) {
|
||||
mPage = getArguments().getInt(ARG_PAGE);
|
||||
LogUtils.d(TAG, "onCreate: 读取页面参数,mPage=" + mPage);
|
||||
}
|
||||
LogUtils.d(TAG, "onCreate: Fragment创建完成");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
// 加载布局(已移除进度条相关代码)
|
||||
View view = inflater.inflate(R.layout.fragment_contacts, container, false);
|
||||
return view;
|
||||
LogUtils.d(TAG, "onCreateView: 加载Fragment布局");
|
||||
return inflater.inflate(R.layout.fragment_contacts, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
LogUtils.d(TAG, "onViewCreated: 开始初始化UI控件");
|
||||
// 初始化RecyclerView
|
||||
recyclerView = (RecyclerView) view.findViewById(R.id.contacts_recycler_view);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
contactList = new ArrayList<ContactModel>();
|
||||
contactAdapter = new ContactAdapter(getActivity(), contactList);
|
||||
recyclerView.setAdapter(contactAdapter);
|
||||
// 初始隐藏列表,数据加载后显示
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
recyclerView.setVisibility(View.GONE); // 初始隐藏列表
|
||||
|
||||
// 绑定搜索框和拨号按钮
|
||||
searchEditText = (EditText) view.findViewById(R.id.search_edit_text);
|
||||
btnDial = (Button) view.findViewById(R.id.btn_dial);
|
||||
// 初始隐藏搜索相关控件,延迟到首次可见时显示
|
||||
searchEditText.setVisibility(View.GONE);
|
||||
btnDial.setVisibility(View.GONE);
|
||||
LogUtils.d(TAG, "onViewCreated: UI控件初始化完成");
|
||||
}
|
||||
|
||||
// 首次可见时初始化资源
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
LogUtils.d(TAG, "onResume: Fragment进入前台");
|
||||
if (!isViewInitialized) {
|
||||
initSearchAndDial(); // 初始化搜索和拨号功能
|
||||
checkContactPermission(); // 检查权限并加载数据
|
||||
initSearchAndDial();
|
||||
checkContactPermission();
|
||||
isViewInitialized = true;
|
||||
LogUtils.d(TAG, "onResume: 首次进入,完成视图和权限初始化");
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化搜索框和拨号按钮
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
LogUtils.d(TAG, "onDestroy: Fragment开始销毁");
|
||||
executor.shutdown(); // 关闭线程池
|
||||
mainHandler.removeCallbacksAndMessages(null); // 清空Handler任务
|
||||
LogUtils.d(TAG, "onDestroy: 异步工具资源已释放");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenChanged(boolean hidden) {
|
||||
super.onHiddenChanged(hidden);
|
||||
LogUtils.d(TAG, "onHiddenChanged: Fragment隐藏状态变更,hidden=" + hidden);
|
||||
if (!hidden && isDataLoaded) {
|
||||
contactList.clear();
|
||||
contactList.addAll(sCachedFilteredList);
|
||||
contactAdapter.notifyDataSetChanged();
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
LogUtils.d(TAG, "onHiddenChanged: 恢复缓存数据,列表已显示");
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 权限相关函数区 ======================
|
||||
private void checkContactPermission() {
|
||||
LogUtils.d(TAG, "checkContactPermission: 检查联系人读取权限");
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
LogUtils.w(TAG, "checkContactPermission: 权限未授予,发起申请");
|
||||
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
|
||||
} else {
|
||||
LogUtils.d(TAG, "checkContactPermission: 权限已授予,开始加载数据");
|
||||
loadContacts();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
LogUtils.d(TAG, "onRequestPermissionsResult: 权限回调触发,requestCode=" + requestCode);
|
||||
if (requestCode == REQUEST_READ_CONTACTS) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
LogUtils.d(TAG, "onRequestPermissionsResult: 联系人权限授予成功");
|
||||
loadContacts();
|
||||
} else {
|
||||
LogUtils.e(TAG, "onRequestPermissionsResult: 联系人权限被拒绝");
|
||||
ToastUtils.show("请授予联系人权限以查看联系人列表");
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== UI功能初始化区 ======================
|
||||
private void initSearchAndDial() {
|
||||
// 显示搜索相关控件
|
||||
LogUtils.d(TAG, "initSearchAndDial: 初始化搜索和拨号功能");
|
||||
// 显示控件
|
||||
searchEditText.setVisibility(View.VISIBLE);
|
||||
btnDial.setVisibility(View.VISIBLE);
|
||||
|
||||
// 搜索框防抖监听
|
||||
searchEditText.addTextChangedListener(new DebounceTextWatcher(300) {
|
||||
// 搜索防抖监听
|
||||
searchEditText.addTextChangedListener(new DebounceTextWatcher(DEBOUNCE_DELAY) {
|
||||
@Override
|
||||
public void onDebounceTextChanged(String query) {
|
||||
filterContacts(query);
|
||||
@@ -142,68 +205,58 @@ public class ContactsFragment extends Fragment {
|
||||
ToastUtils.show("请输入号码");
|
||||
return;
|
||||
}
|
||||
LogUtils.d(TAG, "initSearchAndDial: 发起拨号,号码=" + phoneNumber);
|
||||
Intent intent = new Intent(Intent.ACTION_CALL);
|
||||
intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
LogUtils.d(TAG, "initSearchAndDial: 功能初始化完成");
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
private void checkContactPermission() {
|
||||
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_READ_CONTACTS);
|
||||
} else {
|
||||
loadContacts();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载联系人(延迟到首次可见时)
|
||||
// ====================== 数据加载与处理区 ======================
|
||||
private void loadContacts() {
|
||||
// 若有缓存,直接复用
|
||||
// 优先使用缓存数据
|
||||
if (!sCachedOriginalList.isEmpty() && !sCachedFilteredList.isEmpty()) {
|
||||
LogUtils.d(TAG, "loadContacts: 存在缓存数据,直接复用");
|
||||
originalContactList.clear();
|
||||
originalContactList.addAll(sCachedOriginalList);
|
||||
contactList.clear();
|
||||
contactList.addAll(sCachedFilteredList);
|
||||
contactAdapter.notifyDataSetChanged();
|
||||
recyclerView.setVisibility(View.VISIBLE); // 显示列表
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
isDataLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 无缓存时异步加载
|
||||
if (!isDataLoaded) {
|
||||
recyclerView.setVisibility(View.GONE); // 加载中隐藏列表
|
||||
|
||||
LogUtils.d(TAG, "loadContacts: 无缓存,异步读取联系人数据");
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 子线程读取联系人
|
||||
final List<ContactModel> tempList = readContactsInBackground();
|
||||
|
||||
// 主线程更新UI
|
||||
// 主线程更新UI和缓存
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 更新缓存
|
||||
sCachedOriginalList.clear();
|
||||
sCachedOriginalList.addAll(tempList);
|
||||
sCachedFilteredList.clear();
|
||||
sCachedFilteredList.addAll(tempList);
|
||||
|
||||
// 更新当前列表
|
||||
originalContactList.clear();
|
||||
originalContactList.addAll(sCachedOriginalList);
|
||||
contactList.clear();
|
||||
contactList.addAll(sCachedFilteredList);
|
||||
contactAdapter.notifyDataSetChanged();
|
||||
LogUtils.d(TAG, String.format("联系人加载完成,共%d条数据", contactList.size()));
|
||||
|
||||
// 数据加载后显示列表
|
||||
contactAdapter.notifyDataSetChanged();
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
isDataLoaded = true;
|
||||
|
||||
LogUtils.d(TAG, "loadContacts: 联系人数据加载完成,共" + contactList.size() + "条");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -211,86 +264,71 @@ public class ContactsFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
// 子线程读取联系人
|
||||
private List<ContactModel> readContactsInBackground() {
|
||||
LogUtils.d(TAG, "readContactsInBackground: 子线程读取联系人");
|
||||
List<ContactModel> tempList = new ArrayList<ContactModel>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
// 查询联系人姓名和号码
|
||||
cursor = requireContext().getContentResolver().query(
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER
|
||||
},
|
||||
null,
|
||||
null,
|
||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER
|
||||
},
|
||||
null,
|
||||
null,
|
||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
|
||||
);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
|
||||
int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
|
||||
do {
|
||||
String name = cursor.getString(nameIndex);
|
||||
String number = cursor.getString(numberIndex).replaceAll("\\s", ""); // 去除空格
|
||||
String number = cursor.getString(numberIndex).replaceAll("\\s", "");
|
||||
tempList.add(new ContactModel(name, number));
|
||||
} while (cursor.moveToNext());
|
||||
LogUtils.d(TAG, "readContactsInBackground: 成功读取" + tempList.size() + "条联系人数据");
|
||||
} else {
|
||||
LogUtils.w(TAG, "readContactsInBackground: 未读取到联系人数据");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, "读取联系人失败:" + e);
|
||||
LogUtils.e(TAG, "readContactsInBackground: 读取联系人异常", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close(); // 关闭游标,避免内存泄漏
|
||||
cursor.close();
|
||||
LogUtils.d(TAG, "readContactsInBackground: 游标已关闭");
|
||||
}
|
||||
}
|
||||
return tempList;
|
||||
}
|
||||
|
||||
// 过滤联系人
|
||||
private void filterContacts(String query) {
|
||||
LogUtils.d(TAG, "filterContacts: 搜索过滤,关键词=" + query);
|
||||
contactList.clear();
|
||||
sCachedFilteredList.clear();
|
||||
if (query.isEmpty()) {
|
||||
contactList.addAll(originalContactList);
|
||||
sCachedFilteredList.clear();
|
||||
sCachedFilteredList.addAll(originalContactList);
|
||||
} else {
|
||||
String lowerQuery = query.toLowerCase();
|
||||
for (ContactModel contact : originalContactList) {
|
||||
// 匹配姓名、全拼、简拼、号码
|
||||
boolean matchName = contact.getName().toLowerCase().contains(lowerQuery);
|
||||
boolean matchPinyin = contact.getPinyin().toLowerCase().contains(lowerQuery);
|
||||
boolean matchFirstLetter = contact.getPinyinFirstLetter().toLowerCase().contains(lowerQuery);
|
||||
boolean matchNumber = contact.getNumber().contains(lowerQuery);
|
||||
|
||||
if (matchName || matchPinyin || matchFirstLetter || matchNumber) {
|
||||
contactList.add(contact);
|
||||
}
|
||||
}
|
||||
sCachedFilteredList.clear();
|
||||
sCachedFilteredList.addAll(contactList);
|
||||
}
|
||||
contactAdapter.notifyDataSetChanged();
|
||||
// 过滤后确保列表可见
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
LogUtils.d(TAG, "filterContacts: 过滤完成,显示" + contactList.size() + "条数据");
|
||||
}
|
||||
|
||||
// 权限回调
|
||||
@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) {
|
||||
loadContacts(); // 授权后加载联系人
|
||||
} else {
|
||||
ToastUtils.show("请授予联系人权限以查看联系人列表");
|
||||
recyclerView.setVisibility(View.VISIBLE); // 显示空列表
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 防抖TextWatcher(Java 7实现)
|
||||
// ====================== 内部防抖监听类 ======================
|
||||
public abstract static class DebounceTextWatcher implements TextWatcher {
|
||||
private final long debounceDelay;
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
@@ -301,17 +339,13 @@ public class ContactsFragment extends Fragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// 无需处理
|
||||
}
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, int start, int before, int count) {
|
||||
// 移除之前的延迟任务
|
||||
if (pendingRunnable != null) {
|
||||
handler.removeCallbacks(pendingRunnable);
|
||||
}
|
||||
// 延迟执行过滤
|
||||
pendingRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -322,33 +356,9 @@ public class ContactsFragment extends Fragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// 无需处理
|
||||
}
|
||||
public void afterTextChanged(Editable s) {}
|
||||
|
||||
// 抽象方法:防抖后的回调
|
||||
public abstract void onDebounceTextChanged(String query);
|
||||
}
|
||||
|
||||
// 资源释放
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
executor.shutdown(); // 关闭线程池
|
||||
mainHandler.removeCallbacksAndMessages(null); // 清除未执行任务
|
||||
}
|
||||
|
||||
// Fragment隐藏/显示时的处理
|
||||
@Override
|
||||
public void onHiddenChanged(boolean hidden) {
|
||||
super.onHiddenChanged(hidden);
|
||||
if (!hidden && isDataLoaded) {
|
||||
// 复用缓存数据并显示列表
|
||||
contactList.clear();
|
||||
contactList.addAll(sCachedFilteredList);
|
||||
contactAdapter.notifyDataSetChanged();
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
package cc.winboll.studio.contacts.fragments;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/02/20 12:58:15
|
||||
* @Describe 应用日志
|
||||
*/
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import cc.winboll.studio.contacts.R;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.LogView;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/02/20 12:58:15
|
||||
* @Describe 应用日志区域视图
|
||||
*/
|
||||
public class LogFragment extends Fragment {
|
||||
|
||||
// ====================== 常量定义区 ======================
|
||||
public static final String TAG = "LogFragment";
|
||||
|
||||
private static final String ARG_PAGE = "ARG_PAGE";
|
||||
private int mPage;
|
||||
|
||||
LogView mLogView;
|
||||
|
||||
// ====================== 页面参数区 ======================
|
||||
private int mPage;
|
||||
|
||||
// ====================== UI控件区 ======================
|
||||
private LogView mLogView;
|
||||
|
||||
// ====================== 实例化函数区 ======================
|
||||
public static LogFragment newInstance(int page) {
|
||||
LogUtils.d(TAG, "newInstance: 创建日志Fragment实例,页码=" + page);
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_PAGE, page);
|
||||
LogFragment fragment = new LogFragment();
|
||||
@@ -32,30 +40,53 @@ public class LogFragment extends Fragment {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
// ====================== 生命周期函数区 ======================
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LogUtils.d(TAG, "onCreate: Fragment创建开始");
|
||||
if (getArguments() != null) {
|
||||
mPage = getArguments().getInt(ARG_PAGE);
|
||||
LogUtils.d(TAG, "onCreate: 读取页面参数,mPage=" + mPage);
|
||||
}
|
||||
LogUtils.d(TAG, "onCreate: Fragment创建完成");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
LogUtils.d(TAG, "onCreateView: 加载Fragment布局");
|
||||
View view = inflater.inflate(R.layout.fragment_log, container, false);
|
||||
mLogView = view.findViewById(R.id.logview);
|
||||
// Java7 适配:添加强制类型转换
|
||||
mLogView = (LogView) view.findViewById(R.id.logview);
|
||||
mLogView.start();
|
||||
LogUtils.d(TAG, "onCreateView: LogView已初始化并启动");
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
//ToastUtils.show("onResume");
|
||||
mLogView.start();
|
||||
LogUtils.d(TAG, "onResume: Fragment进入前台");
|
||||
if (mLogView != null) {
|
||||
mLogView.start();
|
||||
LogUtils.d(TAG, "onResume: LogView已重启");
|
||||
} else {
|
||||
LogUtils.w(TAG, "onResume: LogView实例为空,跳过启动");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
LogUtils.d(TAG, "onDestroy: Fragment开始销毁");
|
||||
if (mLogView != null) {
|
||||
// 若LogView有停止方法,建议在此处调用,避免资源泄漏
|
||||
// mLogView.stop();
|
||||
LogUtils.d(TAG, "onDestroy: LogView资源已处理");
|
||||
}
|
||||
LogUtils.d(TAG, "onDestroy: Fragment销毁完成");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user