From fc9f15c70ca5d28fd7b65675bbb1dd0b835402f8 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Fri, 12 Dec 2025 13:01:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BA=90=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../winboll/studio/contacts/MainActivity.java | 373 +++++++++------- .../contacts/activities/SettingsActivity.java | 397 ++++++++++-------- .../contacts/fragments/CallLogFragment.java | 179 +++++--- .../contacts/fragments/ContactsFragment.java | 252 +++++------ .../contacts/fragments/LogFragment.java | 59 ++- 5 files changed, 740 insertions(+), 520 deletions(-) diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java index e76d99b..488f942 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java @@ -1,10 +1,5 @@ package cc.winboll.studio.contacts; -/** - * @Author ZhanGSKen&豆包大模型 - * @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&豆包大模型 + * @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 views; - ImageView[] imageViews; - LinearLayout linearLayout; - int currentPoint = 0; - - private TelephonyManager telephonyManager; - private MyPhoneStateListener phoneStateListener; - List fragmentList; - List 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 views; + private ImageView[] imageViews; + private LinearLayout linearLayout; + // ====================== 业务逻辑成员区 ====================== + private MainServiceBean mMainServiceBean; + private int currentPoint = 0; + private TelephonyManager telephonyManager; + private MyPhoneStateListener phoneStateListener; + private List fragmentList; + private List 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(); tabTitleList = new ArrayList(); - // 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 fragmentList; - private List tabTitleList; + private final List fragmentList; + private final List tabTitleList; public MyPagerAdapter(FragmentManager fm, List fragmentList, List 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(); - } - } } diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java index 76cfd2b..5b1e3e4 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/activities/SettingsActivity.java @@ -1,9 +1,5 @@ package cc.winboll.studio.contacts.activities; -/** - * @Author ZhanGSKen - * @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&豆包大模型 + * @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 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); - } } + diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/CallLogFragment.java b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/CallLogFragment.java index db3ed98..1fe93da 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/CallLogFragment.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/CallLogFragment.java @@ -1,10 +1,5 @@ package cc.winboll.studio.contacts.fragments; -/** - * @Author ZhanGSKen - * @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&豆包大模型 + * @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 callLogList = new ArrayList<>(); + private List callLogList = new ArrayList(); - // 添加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(); // 窗口回显时更新通话记录 - } } + diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java index 8614201..5abcbf2 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/ContactsFragment.java @@ -1,11 +1,5 @@ package cc.winboll.studio.contacts.fragments; - -/** - * @Author ZhanGSKen&豆包大模型 - * @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&豆包大模型 + * @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 sCachedOriginalList = new ArrayList(); + private static List sCachedFilteredList = new ArrayList(); + + // ====================== 页面参数区 ====================== 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 sCachedOriginalList = new ArrayList(); - private static List sCachedFilteredList = new ArrayList(); - - // 当前页面数据容器 + // ====================== 数据容器区 ====================== private List contactList = new ArrayList(); private List originalContactList = new ArrayList(); - // 异步工具 + // ====================== 异步工具区 ====================== 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(); 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 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 readContactsInBackground() { + LogUtils.d(TAG, "readContactsInBackground: 子线程读取联系人"); List tempList = new ArrayList(); 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); - } - } } diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/LogFragment.java b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/LogFragment.java index 81b8369..ae6619b 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/fragments/LogFragment.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/fragments/LogFragment.java @@ -1,30 +1,38 @@ package cc.winboll.studio.contacts.fragments; -/** - * @Author ZhanGSKen - * @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&豆包大模型 + * @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销毁完成"); } - - } +