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