源码整理

This commit is contained in:
2025-12-12 13:01:16 +08:00
parent b872da5dcc
commit fc9f15c70c
5 changed files with 740 additions and 520 deletions

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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(); // 窗口回显时更新通话记录
}
}

View File

@@ -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); // 显示空列表
}
}
}
// 防抖TextWatcherJava 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);
}
}
}

View File

@@ -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销毁完成");
}
}