Compare commits
	
		
			10 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7e061d18bb | |||
|   | 0afe1de9bd | ||
|   | 98874bedc9 | ||
|   | 72cbe4f066 | ||
| b144d6d94c | |||
|   | da7329ffb3 | ||
| 9511b594aa | |||
|   | 46ede050e1 | ||
| eb8d37c340 | |||
|   | d65a839878 | 
| @@ -18,8 +18,8 @@ def genVersionName(def versionName){ | |||||||
| } | } | ||||||
|  |  | ||||||
| android { | android { | ||||||
|     compileSdkVersion 32 |     compileSdkVersion 30 | ||||||
|     buildToolsVersion "32.0.0" |     buildToolsVersion "30.0.3" | ||||||
|  |  | ||||||
|     defaultConfig { |     defaultConfig { | ||||||
|         applicationId "cc.winboll.studio.contacts" |         applicationId "cc.winboll.studio.contacts" | ||||||
| @@ -66,7 +66,7 @@ dependencies { | |||||||
|     // 应用介绍页类库 |     // 应用介绍页类库 | ||||||
|     api 'io.github.medyo:android-about-page:2.0.0' |     api 'io.github.medyo:android-about-page:2.0.0' | ||||||
|     // 吐司类库 |     // 吐司类库 | ||||||
|     api 'com.github.getActivity:ToastUtils:10.5' |     //api 'com.github.getActivity:ToastUtils:10.5' | ||||||
|     // 网络连接类库 |     // 网络连接类库 | ||||||
|     api 'com.squareup.okhttp3:okhttp:4.4.1' |     api 'com.squareup.okhttp3:okhttp:4.4.1' | ||||||
|      |      | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #Created by .winboll/winboll_app_build.gradle | #Created by .winboll/winboll_app_build.gradle | ||||||
| #Sun Aug 31 06:05:42 CST 2025 | #Sat Oct 18 13:43:14 HKT 2025 | ||||||
| stageCount=17 | stageCount=21 | ||||||
| libraryProject= | libraryProject= | ||||||
| baseVersion=15.3 | baseVersion=15.3 | ||||||
| publishVersion=15.3.16 | publishVersion=15.3.20 | ||||||
| buildCount=0 | buildCount=0 | ||||||
| baseBetaVersion=15.3.17 | baseBetaVersion=15.3.21 | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ package cc.winboll.studio.contacts; | |||||||
|  */ |  */ | ||||||
| import android.view.Gravity; | import android.view.Gravity; | ||||||
| import cc.winboll.studio.libappbase.GlobalApplication; | import cc.winboll.studio.libappbase.GlobalApplication; | ||||||
|  | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import cc.winboll.studio.libappbase.winboll.WinBoLLActivityManager; | import cc.winboll.studio.libappbase.winboll.WinBoLLActivityManager; | ||||||
| import com.hjq.toast.ToastUtils; |  | ||||||
|  |  | ||||||
| public class App extends GlobalApplication { | public class App extends GlobalApplication { | ||||||
|  |  | ||||||
| @@ -30,7 +30,7 @@ public class App extends GlobalApplication { | |||||||
|         // 设置 Toast 布局样式 |         // 设置 Toast 布局样式 | ||||||
|         //ToastUtils.setView(R.layout.toast_custom_view); |         //ToastUtils.setView(R.layout.toast_custom_view); | ||||||
|         //ToastUtils.setStyle(new WhiteToastStyle()); |         //ToastUtils.setStyle(new WhiteToastStyle()); | ||||||
|         ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); |         //ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); | ||||||
|          |          | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ package cc.winboll.studio.contacts; | |||||||
| import android.Manifest; | import android.Manifest; | ||||||
| import android.app.Activity; | import android.app.Activity; | ||||||
| import android.app.ActivityManager; | import android.app.ActivityManager; | ||||||
|  | import android.app.AlertDialog; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.pm.PackageManager; | import android.content.pm.PackageManager; | ||||||
| @@ -31,6 +32,8 @@ 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 // 新增:导入 AppGoToSettingsUtil 工具类(确保包路径与项目实际一致) | ||||||
|  | cc.winboll.studio.contacts.utils.AppGoToSettingsUtil; | ||||||
| import cc.winboll.studio.contacts.R; | 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; | ||||||
| @@ -48,10 +51,9 @@ import java.util.List; | |||||||
| final public class MainActivity extends AppCompatActivity implements IWinBoLLActivity, ViewPager.OnPageChangeListener, View.OnClickListener { | final public 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 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; |     static MainActivity _MainActivity; | ||||||
| @@ -72,6 +74,13 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | |||||||
|     List<String> tabTitleList; |     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; | ||||||
|  |     // 关键修改1:新增 READ_CALL_LOG 权限到必需权限列表(解决通话记录读取崩溃) | ||||||
|  |     private String[] REQUIRED_PERMISSIONS = new String[]{ | ||||||
|  | 		Manifest.permission.READ_CONTACTS,  // 通讯录读取(原) | ||||||
|  | 		Manifest.permission.CALL_PHONE,     // 电话拨号(原) | ||||||
|  | 		Manifest.permission.READ_CALL_LOG   // 通话记录读取(新增,核心修复) | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -88,9 +97,86 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | |||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
|         _MainActivity = this; |         _MainActivity = this; | ||||||
|  |  | ||||||
|  |         // 优先检查所有必需权限(含新增的 READ_CALL_LOG) | ||||||
|  |         if (!checkAllRequiredPermissions()) { | ||||||
|  |             requestAllRequiredPermissions(); | ||||||
|  |         } else { | ||||||
|  |             initUIAndLogic(savedInstanceState); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 权限检查方法(无需修改,自动包含新增的 READ_CALL_LOG) | ||||||
|  |     private boolean checkAllRequiredPermissions() { | ||||||
|  |         for (String permission : REQUIRED_PERMISSIONS) { | ||||||
|  |             if (ActivityCompat.checkSelfPermission(this, permission)  | ||||||
|  | 				!= PackageManager.PERMISSION_GRANTED) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 权限申请方法(无需修改,自动申请新增的 READ_CALL_LOG) | ||||||
|  |     private void requestAllRequiredPermissions() { | ||||||
|  |         ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_REQUIRED_PERMISSIONS); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 权限结果回调(无需修改,确保所有权限(含 READ_CALL_LOG)都通过才加载UI) | ||||||
|  |     @Override | ||||||
|  |     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { | ||||||
|  |         super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||||
|  |  | ||||||
|  |         if (requestCode == REQUEST_REQUIRED_PERMISSIONS) { | ||||||
|  |             boolean allPermissionsGranted = true; | ||||||
|  |             for (int result : grantResults) { | ||||||
|  |                 if (result != PackageManager.PERMISSION_GRANTED) { | ||||||
|  |                     allPermissionsGranted = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (allPermissionsGranted) { | ||||||
|  |                 initUIAndLogic(null); | ||||||
|  |             } else { | ||||||
|  |                 // 关键修改2:更新提示文案,告知用户新增的“通话记录权限” | ||||||
|  |                 showPermissionDeniedDialogAndExit(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 核心修改:新增“设置权限”按钮,点击调用 AppGoToSettingsUtil 跳转设置页 | ||||||
|  |     private void showPermissionDeniedDialogAndExit() { | ||||||
|  |         new AlertDialog.Builder(this) | ||||||
|  | 			.setTitle("权限不足,无法使用") | ||||||
|  | 			// 文案修改:明确新增“通话记录读取”权限 | ||||||
|  | 			.setMessage("应用需要「通讯录读取」、「电话」和「通话记录读取」权限才能正常运行,请授予权限后重新打开应用。") | ||||||
|  | 			.setCancelable(false) | ||||||
|  | 			// 新增:左侧“设置权限”按钮(先添加的按钮在左侧) | ||||||
|  | 			.setNegativeButton("设置权限", new AlertDialog.OnClickListener() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onClick(android.content.DialogInterface dialog, int which) { | ||||||
|  | 					dialog.dismiss(); | ||||||
|  | 					// 调用工具类跳转应用设置页(按需求实现) | ||||||
|  | 					AppGoToSettingsUtil appGoToSettingsUtil = new AppGoToSettingsUtil(); | ||||||
|  | 					appGoToSettingsUtil.GoToSetting(MainActivity.this); | ||||||
|  | 				} | ||||||
|  | 			}) | ||||||
|  | 			// 原有:右侧“确定退出”按钮(后添加的按钮在右侧) | ||||||
|  | 			.setPositiveButton("确定退出", new AlertDialog.OnClickListener() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onClick(android.content.DialogInterface dialog, int which) { | ||||||
|  | 					dialog.dismiss(); | ||||||
|  | 					finishAndRemoveTask(); | ||||||
|  | 				} | ||||||
|  | 			}) | ||||||
|  | 			.show(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 初始化UI和逻辑(无需修改,权限通过后才加载 CallLogFragment) | ||||||
|  |     private void initUIAndLogic(Bundle savedInstanceState) { | ||||||
|         setContentView(R.layout.activity_main); |         setContentView(R.layout.activity_main); | ||||||
|  |  | ||||||
|         // 初始化工具栏(仅加载基础UI) |  | ||||||
|         mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1); |         mToolbar = (Toolbar) findViewById(R.id.activitymainToolbar1); | ||||||
|         setSupportActionBar(mToolbar); |         setSupportActionBar(mToolbar); | ||||||
|         getSupportActionBar().setSubtitle(TAG); |         getSupportActionBar().setSubtitle(TAG); | ||||||
| @@ -98,34 +184,28 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | |||||||
|         tabLayout = (TabLayout) findViewById(R.id.tabLayout); |         tabLayout = (TabLayout) findViewById(R.id.tabLayout); | ||||||
|         viewPager = (ViewPager) findViewById(R.id.viewPager); |         viewPager = (ViewPager) findViewById(R.id.viewPager); | ||||||
|  |  | ||||||
|         // 创建Fragment列表(仅实例化,不加载数据) |  | ||||||
|         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)); | ||||||
|         tabTitleList.add("通话记录"); |         tabTitleList.add("通话记录"); | ||||||
|         tabTitleList.add("联系人"); |         tabTitleList.add("联系人"); | ||||||
|         tabTitleList.add("应用日志"); |         tabTitleList.add("应用日志"); | ||||||
|  |  | ||||||
|         // 设置ViewPager适配器 |  | ||||||
|         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和ViewPager |  | ||||||
|         tabLayout.setupWithViewPager(viewPager); |         tabLayout.setupWithViewPager(viewPager); | ||||||
|  |  | ||||||
|         // 初始化服务状态(延迟启动非核心服务) |         // 原有服务启动、电话监听等逻辑... | ||||||
|         MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); |         MainServiceBean mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class); | ||||||
|         if (mMainServiceBean == null) { |         if (mMainServiceBean == null) { | ||||||
|             mMainServiceBean = new MainServiceBean(); |             mMainServiceBean = new MainServiceBean(); | ||||||
|             MainServiceBean.saveBean(this, mMainServiceBean); |             MainServiceBean.saveBean(this, mMainServiceBean); | ||||||
|         } |         } | ||||||
|         if (mMainServiceBean.isEnable()) { |         if (mMainServiceBean.isEnable()) { | ||||||
|             // 延迟1秒启动服务,避免阻塞启动 |  | ||||||
|             new Handler().postDelayed(new Runnable() { |             new Handler().postDelayed(new Runnable() { | ||||||
| 					@Override | 					@Override | ||||||
| 					public void run() { | 					public void run() { | ||||||
| @@ -134,16 +214,14 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | |||||||
| 				}, 1000); | 				}, 1000); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 初始化电话状态监听(基础功能保留) |  | ||||||
|         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); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     // ViewPager适配器(Java 7语法) |     // 以下为原有代码(无需修改) | ||||||
|     private class MyPagerAdapter extends FragmentPagerAdapter { |     private class MyPagerAdapter extends FragmentPagerAdapter { | ||||||
|  |  | ||||||
|         private List<Fragment> fragmentList; |         private List<Fragment> fragmentList; | ||||||
|         private List<String> tabTitleList; |         private List<String> tabTitleList; | ||||||
|  |  | ||||||
| @@ -173,21 +251,18 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | |||||||
|         Intent intent = new Intent(Intent.ACTION_DIAL); |         Intent intent = new Intent(Intent.ACTION_DIAL); | ||||||
|         intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); |         intent.setData(android.net.Uri.parse("tel:" + phoneNumber)); | ||||||
|         if (ActivityCompat.checkSelfPermission(_MainActivity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { |         if (ActivityCompat.checkSelfPermission(_MainActivity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { | ||||||
|  |             Toast.makeText(_MainActivity, "拨号权限不足", Toast.LENGTH_SHORT).show(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         _MainActivity.startActivity(intent); |         _MainActivity.startActivity(intent); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // OnPageChangeListener接口实现 |  | ||||||
|     @Override |     @Override | ||||||
|     public void onPageScrollStateChanged(int state) {} |     public void onPageScrollStateChanged(int state) {} | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} |     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onPageSelected(int position) {} |     public void onPageSelected(int position) {} | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onClick(View v) {} |     public void onClick(View v) {} | ||||||
|  |  | ||||||
| @@ -239,9 +314,6 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | |||||||
|         super.onResume(); |         super.onResume(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 检查是否是系统默认电话应用 |  | ||||||
|      */ |  | ||||||
|     public boolean isDefaultPhoneCallApp() { |     public boolean isDefaultPhoneCallApp() { | ||||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||||||
|             TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE); |             TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE); | ||||||
| @@ -272,7 +344,9 @@ final public class MainActivity extends AppCompatActivity implements IWinBoLLAct | |||||||
|                 Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用", |                 Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用", | ||||||
| 							   Toast.LENGTH_SHORT).show(); | 							   Toast.LENGTH_SHORT).show(); | ||||||
|             } |             } | ||||||
|         } |         } else if (requestCode == REQUEST_APP_SETTINGS) { | ||||||
|  | 			recreate(); | ||||||
|  | 		} | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -36,8 +36,8 @@ import cc.winboll.studio.contacts.dun.Rules; | |||||||
| import cc.winboll.studio.contacts.services.MainService; | import cc.winboll.studio.contacts.services.MainService; | ||||||
| import cc.winboll.studio.contacts.views.DuInfoTextView; | import cc.winboll.studio.contacts.views.DuInfoTextView; | ||||||
| import cc.winboll.studio.libappbase.LogUtils; | import cc.winboll.studio.libappbase.LogUtils; | ||||||
|  | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; | ||||||
| import com.hjq.toast.ToastUtils; |  | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ import cc.winboll.studio.contacts.R; | |||||||
| import cc.winboll.studio.contacts.beans.CallLogModel; | import cc.winboll.studio.contacts.beans.CallLogModel; | ||||||
| import cc.winboll.studio.contacts.utils.ContactUtils; | import cc.winboll.studio.contacts.utils.ContactUtils; | ||||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||||
| import com.hjq.toast.ToastUtils; | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Locale; | import java.util.Locale; | ||||||
| @@ -76,6 +76,9 @@ public class CallLogAdapter extends RecyclerView.Adapter<CallLogAdapter.CallLogV | |||||||
| 									// Set the clipboard's primary clip. | 									// Set the clipboard's primary clip. | ||||||
| 									clipboard.setPrimaryClip(clip); | 									clipboard.setPrimaryClip(clip); | ||||||
| 									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show(); | 									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show(); | ||||||
|  | 								} else if (nItemId == R.id.item_calllog_phonenumber_add_contact) { | ||||||
|  | 									//ToastUtils.show(callLog.getPhoneNumber()); | ||||||
|  | 									ContactUtils.jumpToAddContact(mContext, callLog.getPhoneNumber()); | ||||||
| 								} | 								} | ||||||
|  |  | ||||||
| 								return true; | 								return true; | ||||||
|   | |||||||
| @@ -21,8 +21,9 @@ import androidx.annotation.NonNull; | |||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.RecyclerView; | ||||||
| import cc.winboll.studio.contacts.R; | import cc.winboll.studio.contacts.R; | ||||||
| import cc.winboll.studio.contacts.beans.ContactModel; | import cc.winboll.studio.contacts.beans.ContactModel; | ||||||
|  | import cc.winboll.studio.contacts.utils.ContactUtils; | ||||||
| import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; | ||||||
| import com.hjq.toast.ToastUtils; | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> { | public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> { | ||||||
| @@ -69,6 +70,11 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV | |||||||
| 									// Set the clipboard's primary clip. | 									// Set the clipboard's primary clip. | ||||||
| 									clipboard.setPrimaryClip(clip); | 									clipboard.setPrimaryClip(clip); | ||||||
| 									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show(); | 									Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show(); | ||||||
|  | 								} else if (nItemId == R.id.item_calllog_phonenumber_edit_contact) { | ||||||
|  | 									//ToastUtils.show("Test"); | ||||||
|  | 									Long nContactId = ContactUtils.getContactIdByPhone(mContext, contact.getNumber()); | ||||||
|  | 									//ToastUtils.show(String.format("%d", nContactId)); | ||||||
|  | 									ContactUtils.jumpToEditContact(mContext, contact.getNumber(), nContactId); | ||||||
| 								} | 								} | ||||||
|  |  | ||||||
| 								return true; | 								return true; | ||||||
| @@ -112,7 +118,7 @@ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactV | |||||||
|         TextView contactName; |         TextView contactName; | ||||||
|         TextView contactNumber; |         TextView contactNumber; | ||||||
|         AOHPCTCSeekBar dialAOHPCTCSeekBar; |         AOHPCTCSeekBar dialAOHPCTCSeekBar; | ||||||
|          |  | ||||||
|         public ContactViewHolder(@NonNull View itemView) { |         public ContactViewHolder(@NonNull View itemView) { | ||||||
|             super(itemView); |             super(itemView); | ||||||
| 			llPhoneNumberMain = itemView.findViewById(R.id.itemcontactLinearLayout1); | 			llPhoneNumberMain = itemView.findViewById(R.id.itemcontactLinearLayout1); | ||||||
|   | |||||||
| @@ -7,7 +7,6 @@ package cc.winboll.studio.contacts.adapters; | |||||||
|  */ |  */ | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.MotionEvent; |  | ||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.Button; | import android.widget.Button; | ||||||
| @@ -21,9 +20,8 @@ import cc.winboll.studio.contacts.R; | |||||||
| import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel; | import cc.winboll.studio.contacts.beans.PhoneConnectRuleModel; | ||||||
| import cc.winboll.studio.contacts.dun.Rules; | import cc.winboll.studio.contacts.dun.Rules; | ||||||
| import cc.winboll.studio.contacts.views.LeftScrollView; | import cc.winboll.studio.contacts.views.LeftScrollView; | ||||||
| import cc.winboll.studio.libappbase.LogUtils; |  | ||||||
| import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog; | import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog; | ||||||
| import com.hjq.toast.ToastUtils; | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import android.content.Context; | |||||||
| import cc.winboll.studio.contacts.R; | import cc.winboll.studio.contacts.R; | ||||||
| import cc.winboll.studio.contacts.dun.Rules; | import cc.winboll.studio.contacts.dun.Rules; | ||||||
| import cc.winboll.studio.libappbase.LogUtils; | import cc.winboll.studio.libappbase.LogUtils; | ||||||
| import com.hjq.toast.ToastUtils; | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.FileFilter; | import java.io.FileFilter; | ||||||
| import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||||
|   | |||||||
| @@ -25,7 +25,6 @@ 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 com.hjq.toast.ToastUtils; |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -161,7 +160,7 @@ public class CallLogFragment extends Fragment { | |||||||
|             _CallLogFragment.triggerUpdate(); |             _CallLogFragment.triggerUpdate(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 	 |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void onResume() { | 	public void onResume() { | ||||||
| 		super.onResume(); | 		super.onResume(); | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ import cc.winboll.studio.contacts.R; | |||||||
| import cc.winboll.studio.contacts.adapters.ContactAdapter; | import cc.winboll.studio.contacts.adapters.ContactAdapter; | ||||||
| import cc.winboll.studio.contacts.beans.ContactModel; | import cc.winboll.studio.contacts.beans.ContactModel; | ||||||
| import cc.winboll.studio.libappbase.LogUtils; | import cc.winboll.studio.libappbase.LogUtils; | ||||||
| import com.hjq.toast.ToastUtils; | import cc.winboll.studio.libappbase.utils.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; | ||||||
| @@ -95,7 +95,7 @@ public class ContactsFragment extends Fragment { | |||||||
|         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>(); |         contactList = new ArrayList<ContactModel>(); | ||||||
|         contactAdapter = new ContactAdapter(getContext(), contactList); |         contactAdapter = new ContactAdapter(getActivity(), contactList); | ||||||
|         recyclerView.setAdapter(contactAdapter); |         recyclerView.setAdapter(contactAdapter); | ||||||
|         // 初始隐藏列表,数据加载后显示 |         // 初始隐藏列表,数据加载后显示 | ||||||
|         recyclerView.setVisibility(View.GONE); |         recyclerView.setVisibility(View.GONE); | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ 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.LogView; | import cc.winboll.studio.libappbase.LogView; | ||||||
| import com.hjq.toast.ToastUtils; |  | ||||||
|  |  | ||||||
| public class LogFragment extends Fragment { | public class LogFragment extends Fragment { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ import android.content.Context; | |||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.IntentFilter; | import android.content.IntentFilter; | ||||||
| import cc.winboll.studio.contacts.services.MainService; | import cc.winboll.studio.contacts.services.MainService; | ||||||
| import com.hjq.toast.ToastUtils; | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import java.lang.ref.WeakReference; | import java.lang.ref.WeakReference; | ||||||
|  |  | ||||||
| public class MainReceiver extends BroadcastReceiver { | public class MainReceiver extends BroadcastReceiver { | ||||||
|   | |||||||
| @@ -0,0 +1,270 @@ | |||||||
|  | package cc.winboll.studio.contacts.utils; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> | ||||||
|  |  * @Date 2025/09/27 14:27 | ||||||
|  |  * @Describe 调用应用属性设置页工具类 | ||||||
|  |  *  来源:https://blog.csdn.net/zhuhai__yizhi/article/details/78737593 | ||||||
|  |  *  Created by zyy on 2018/3/12. | ||||||
|  |  *  直接跳转到权限后返回,可以监控权限授权情况,但是,跳转到应用详情页,无法监测权限情况 | ||||||
|  |  *  是否要加以区分,若是应用详情页,则跳转回来后,onRestart检测所求权限,如果授权,则收回提示,如果没授权,则继续提示 | ||||||
|  |  */ | ||||||
|  | import android.app.Activity; | ||||||
|  | import android.content.ComponentName; | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.net.Uri; | ||||||
|  | import android.os.Build; | ||||||
|  | import android.provider.Settings; | ||||||
|  | import cc.winboll.studio.contacts.MainActivity; | ||||||
|  |  | ||||||
|  | public class AppGoToSettingsUtil { | ||||||
|  |  | ||||||
|  |     public static final String TAG = "AppGoToSettingsUtil"; | ||||||
|  |  | ||||||
|  |     public static final int ACTIVITY_RESULT_APP_SETTINGS = MainActivity.REQUEST_APP_SETTINGS; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Build.MANUFACTURER判断各大手机厂商品牌 | ||||||
|  |      */ | ||||||
|  |     private static final String MANUFACTURER_HUAWEI = "Huawei";//华为 | ||||||
|  |     private static final String MANUFACTURER_MEIZU = "Meizu";//魅族 | ||||||
|  |     private static final String MANUFACTURER_XIAOMI = "Xiaomi";//小米 | ||||||
|  |     private static final String MANUFACTURER_SONY = "Sony";//索尼 | ||||||
|  |     private static final String MANUFACTURER_OPPO = "OPPO"; | ||||||
|  |     private static final String MANUFACTURER_LG = "LG"; | ||||||
|  |     private static final String MANUFACTURER_VIVO = "vivo"; | ||||||
|  |     private static final String MANUFACTURER_SAMSUNG = "samsung";//三星 | ||||||
|  |     private static final String MANUFACTURER_LETV = "Letv";//乐视 | ||||||
|  |     private static final String MANUFACTURER_ZTE = "ZTE";//中兴 | ||||||
|  |     private static final String MANUFACTURER_YULONG = "YuLong";//酷派 | ||||||
|  |     private static final String MANUFACTURER_LENOVO = "LENOVO";//联想 | ||||||
|  |  | ||||||
|  |     public static boolean isAppSettingOpen=false; | ||||||
|  |     /** | ||||||
|  |      * 跳转到相应品牌手机系统权限设置页,如果跳转不成功,则跳转到应用详情页 | ||||||
|  |      * 这里需要改造成返回true或者false,应用详情页:true,应用权限页:false | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void GoToSetting(Activity activity) { | ||||||
|  |         switch (Build.MANUFACTURER) { | ||||||
|  |             case MANUFACTURER_HUAWEI://华为 | ||||||
|  |                 Huawei(activity); | ||||||
|  |                 break; | ||||||
|  |             case MANUFACTURER_MEIZU://魅族 | ||||||
|  |                 Meizu(activity); | ||||||
|  |                 break; | ||||||
|  |             case MANUFACTURER_XIAOMI://小米 | ||||||
|  |                 Xiaomi(activity); | ||||||
|  |                 break; | ||||||
|  |             case MANUFACTURER_SONY://索尼 | ||||||
|  |                 Sony(activity); | ||||||
|  |                 break; | ||||||
|  |             case MANUFACTURER_OPPO://oppo | ||||||
|  |                 OPPO(activity); | ||||||
|  |                 break; | ||||||
|  |             case MANUFACTURER_LG://lg | ||||||
|  |                 LG(activity); | ||||||
|  |                 break; | ||||||
|  |             case MANUFACTURER_LETV://乐视 | ||||||
|  |                 Letv(activity); | ||||||
|  |                 break; | ||||||
|  |             default://其他 | ||||||
|  |                 try {//防止应用详情页也找不到,捕获异常后跳转到设置,这里跳转最好是两级,太多用户也会觉得麻烦,还不如不跳 | ||||||
|  |                     openAppDetailSetting(activity); | ||||||
|  |                     //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |                 } catch (Exception e) { | ||||||
|  |                     SystemConfig(activity); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 华为跳转权限设置页 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void Huawei(Activity activity) { | ||||||
|  |         try { | ||||||
|  |             Intent intent = new Intent(); | ||||||
|  |             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |             intent.putExtra("packageName", activity.getPackageName()); | ||||||
|  |             ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity"); | ||||||
|  |             intent.setComponent(comp); | ||||||
|  |             activity.startActivityForResult(intent, ACTIVITY_RESULT_APP_SETTINGS); | ||||||
|  |             isAppSettingOpen = false; | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             openAppDetailSetting(activity); | ||||||
|  |             //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 魅族跳转权限设置页,测试时,点击无反应,具体原因不明 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void Meizu(Activity activity) { | ||||||
|  |         try { | ||||||
|  |             Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); | ||||||
|  |             intent.addCategory(Intent.CATEGORY_DEFAULT); | ||||||
|  |             intent.putExtra("packageName", activity.getPackageName()); | ||||||
|  |             activity.startActivity(intent); | ||||||
|  |             isAppSettingOpen = false; | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             openAppDetailSetting(activity); | ||||||
|  |             //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 小米,功能正常 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void Xiaomi(Activity activity) { | ||||||
|  |         try { //MIUI 8 9 | ||||||
|  |             Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR"); | ||||||
|  |             localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); | ||||||
|  |             localIntent.putExtra("extra_pkgname", activity.getPackageName()); | ||||||
|  |             activity.startActivityForResult(localIntent, ACTIVITY_RESULT_APP_SETTINGS); | ||||||
|  |             isAppSettingOpen = false; | ||||||
|  |             //activity.startActivity(localIntent); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             try { //MIUI 5/6/7 | ||||||
|  |                 Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR"); | ||||||
|  |                 localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); | ||||||
|  |                 localIntent.putExtra("extra_pkgname", activity.getPackageName()); | ||||||
|  |                 activity.startActivityForResult(localIntent, ACTIVITY_RESULT_APP_SETTINGS); | ||||||
|  |                 isAppSettingOpen = false; | ||||||
|  |                 //activity.startActivity(localIntent); | ||||||
|  |             } catch (Exception e1) { //否则跳转到应用详情 | ||||||
|  |                 openAppDetailSetting(activity); | ||||||
|  |                 //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |                 //这里有个问题,进入活动后需要再跳一级活动,就检测不到返回结果 | ||||||
|  |                 //activity.startActivity(getAppDetailSettingIntent()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 索尼,6.0以上的手机非常少,基本没看见 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void Sony(Activity activity) { | ||||||
|  |         try { | ||||||
|  |             Intent intent = new Intent(); | ||||||
|  |             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |             intent.putExtra("packageName", activity.getPackageName()); | ||||||
|  |             ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity"); | ||||||
|  |             intent.setComponent(comp); | ||||||
|  |             activity.startActivity(intent); | ||||||
|  |             isAppSettingOpen = false; | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             openAppDetailSetting(activity); | ||||||
|  |             //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * OPPO | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void OPPO(Activity activity) { | ||||||
|  |         try { | ||||||
|  |             Intent intent = new Intent(); | ||||||
|  |             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |             intent.putExtra("packageName", activity.getPackageName()); | ||||||
|  |             ComponentName comp = new ComponentName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity"); | ||||||
|  |             intent.setComponent(comp); | ||||||
|  |             activity.startActivity(intent); | ||||||
|  |             isAppSettingOpen = false; | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             openAppDetailSetting(activity); | ||||||
|  |             //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * LG经过测试,正常使用 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void LG(Activity activity) { | ||||||
|  |         try { | ||||||
|  |             Intent intent = new Intent("android.intent.action.MAIN"); | ||||||
|  |             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |             intent.putExtra("packageName", activity.getPackageName()); | ||||||
|  |             ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity"); | ||||||
|  |             intent.setComponent(comp); | ||||||
|  |             activity.startActivity(intent); | ||||||
|  |             isAppSettingOpen = false; | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             openAppDetailSetting(activity); | ||||||
|  |             //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 乐视6.0以上很少,基本都可以忽略了,现在乐视手机不多 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void Letv(Activity activity) { | ||||||
|  |         try { | ||||||
|  |             Intent intent = new Intent(); | ||||||
|  |             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |             intent.putExtra("packageName", activity.getPackageName()); | ||||||
|  |             ComponentName comp = new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps"); | ||||||
|  |             intent.setComponent(comp); | ||||||
|  |             activity.startActivity(intent); | ||||||
|  |             isAppSettingOpen = false; | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             openAppDetailSetting(activity); | ||||||
|  |             //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 只能打开到自带安全软件 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void _360(Activity activity) { | ||||||
|  |         try { | ||||||
|  |             Intent intent = new Intent("android.intent.action.MAIN"); | ||||||
|  |             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |             intent.putExtra("packageName", activity.getPackageName()); | ||||||
|  |             ComponentName comp = new ComponentName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity"); | ||||||
|  |             intent.setComponent(comp); | ||||||
|  |             activity.startActivity(intent); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             openAppDetailSetting(activity); | ||||||
|  |             //activity.startActivityForResult(getAppDetailSettingIntent(activity), PERMISSION_SETTING_FOR_RESULT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * 系统设置界面 | ||||||
|  |      * @param activity | ||||||
|  |      */ | ||||||
|  |     public static void SystemConfig(Activity activity) { | ||||||
|  |         Intent intent = new Intent(Settings.ACTION_SETTINGS); | ||||||
|  |         activity.startActivity(intent); | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * 获取应用详情页面 | ||||||
|  |      * @return | ||||||
|  |      */ | ||||||
|  |     private static Intent getAppDetailSettingIntent(Activity activity) { | ||||||
|  |         Intent localIntent = new Intent(); | ||||||
|  |         localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |         //if (Build.VERSION.SDK_INT >= 9) { | ||||||
|  |         localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); | ||||||
|  |         localIntent.setData(Uri.fromParts("package", activity.getPackageName(), null)); | ||||||
|  |         /*} else if (Build.VERSION.SDK_INT <= 8) { | ||||||
|  |          localIntent.setAction(Intent.ACTION_VIEW); | ||||||
|  |          localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); | ||||||
|  |          localIntent.putExtra("com.android.settings.ApplicationPkgName", activity.getPackageName()); | ||||||
|  |          }*/ | ||||||
|  |         return localIntent; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static void openAppDetailSetting(Activity activity) { | ||||||
|  |         activity.startActivityForResult(getAppDetailSettingIntent(activity), ACTIVITY_RESULT_APP_SETTINGS); | ||||||
|  |         isAppSettingOpen = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -6,10 +6,14 @@ package cc.winboll.studio.contacts.utils; | |||||||
|  * @Describe 联系人工具集 |  * @Describe 联系人工具集 | ||||||
|  */ |  */ | ||||||
| import android.content.ContentResolver; | import android.content.ContentResolver; | ||||||
|  | import android.content.ContentUris; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
|  | import android.content.Intent; | ||||||
| import android.database.Cursor; | import android.database.Cursor; | ||||||
|  | import android.net.Uri; | ||||||
| import android.provider.ContactsContract; | import android.provider.ContactsContract; | ||||||
| import cc.winboll.studio.libappbase.LogUtils; | import cc.winboll.studio.libappbase.LogUtils; | ||||||
|  | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| @@ -120,4 +124,92 @@ public class ContactUtils { | |||||||
|         } |         } | ||||||
|         return sbSpaceNumber.toString(); |         return sbSpaceNumber.toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 跳转至系统添加联系人界面的工具函数 | ||||||
|  | 	 * @param context 上下文(如 PhoneCallService、Activity、Fragment 均可,需传入有效上下文) | ||||||
|  | 	 * @param phoneNumber 可选参数:预填的联系人电话(传 null 则跳转空表单) | ||||||
|  | 	 */ | ||||||
|  | 	public static void jumpToAddContact(Context mContext, String phoneNumber) { | ||||||
|  | 		Intent intent = new Intent(Intent.ACTION_INSERT); | ||||||
|  | 		intent.setType("vnd.android.cursor.dir/person"); | ||||||
|  | 		intent.putExtra(android.provider.ContactsContract.Intents.Insert.PHONE, phoneNumber); | ||||||
|  | 		mContext.startActivity(intent); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 跳转至系统编辑联系人界面(适配小米等定制机型) | ||||||
|  | 	 * @param context 上下文(Activity/Service/Fragment) | ||||||
|  | 	 * @param phoneNumber 待编辑联系人的电话号码(用于匹配已有联系人,必传) | ||||||
|  | 	 * @param contactId 可选:已有联系人的ID(通过 ContactsContract 获取,传null则自动匹配号码) | ||||||
|  | 	 */ | ||||||
|  | 	public static void jumpToEditContact(Context context, String phoneNumber, Long contactId) { | ||||||
|  | 		Intent intent = new Intent(Intent.ACTION_EDIT); | ||||||
|  | 		// 关键:小米等机型需明确设置数据类型为“单个联系人”,避免参数丢失 | ||||||
|  | 		intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); | ||||||
|  |  | ||||||
|  | 		// 场景A:已知联系人ID(精准定位,优先用此方式,参数传递最稳定) | ||||||
|  | 		if (contactId != null && contactId > 0) { | ||||||
|  | 			// 构建联系人的Uri(格式:content://contacts/people/[contactId],系统标准格式) | ||||||
|  | 			Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId); | ||||||
|  | 			intent.setData(contactUri); | ||||||
|  | 			//ToastUtils.show("1"); | ||||||
|  | 		} else if (phoneNumber != null && !phoneNumber.isEmpty()) { | ||||||
|  | 			// 方式1:小米等机型兼容的“通过号码定位联系人”参数(部分系统认此参数) | ||||||
|  | 			//intent.putExtra(ContactsContract.Intents.Insert.PHONE_NUMBER, phoneNumber); | ||||||
|  | 			// 方式2:补充系统标准的“数据Uri”,强化匹配(避免参数被定制系统忽略) | ||||||
|  | 			Uri phoneUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); | ||||||
|  | 			intent.setData(phoneUri); | ||||||
|  | 		} else { | ||||||
|  | 			LogUtils.d(TAG, "编辑联系人失败:电话号码和联系人ID均为空"); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 可选:预填最新号码(覆盖原有号码,若用户修改了号码,编辑时自动更新) | ||||||
|  | 		if (phoneNumber != null && !phoneNumber.isEmpty()) { | ||||||
|  | 			intent.putExtra(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber); | ||||||
|  | 			intent.putExtra(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 启动活动(加防护,避免无联系人应用崩溃) | ||||||
|  | 		// 小米机型在Service/非Activity中调用,需加NEW_TASK标志,否则可能无法启动 | ||||||
|  | 		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  | 		context.startActivity(intent); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 通过电话号码查询联系人ID(适配小米机型,解决编辑时匹配不稳定问题) | ||||||
|  | 	 * @param context 上下文 | ||||||
|  | 	 * @param phoneNumber 待查询的电话号码 | ||||||
|  | 	 * @return 联系人ID(无匹配时返回-1) | ||||||
|  | 	 */ | ||||||
|  | 	public static Long getContactIdByPhone(Context context, String phoneNumber) { | ||||||
|  | 		if (phoneNumber == null || phoneNumber.isEmpty()) { | ||||||
|  | 			return -1L; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ContentResolver cr = context.getContentResolver(); | ||||||
|  | 		// 1. 构建电话查询Uri(系统标准:通过号码过滤联系人数据) | ||||||
|  | 		Uri queryUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); | ||||||
|  | 		// 2. 只查询“联系人ID”字段(高效,避免冗余数据) | ||||||
|  | 		String[] projection = {ContactsContract.CommonDataKinds.Phone.CONTACT_ID}; | ||||||
|  | 		Cursor cursor = null; | ||||||
|  |  | ||||||
|  | 		try { | ||||||
|  | 			cursor = cr.query(queryUri, projection, null, null, null); | ||||||
|  | 			if (cursor != null && cursor.moveToFirst()) { | ||||||
|  | 				// 3. 读取联系人ID(返回Long类型,避免int溢出) | ||||||
|  | 				return cursor.getLong(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID)); | ||||||
|  | 			} | ||||||
|  | 		} catch (Exception e) { | ||||||
|  | 			LogUtils.d(TAG, "查询联系人ID失败。" + e); | ||||||
|  | 		} finally { | ||||||
|  | 			if (cursor != null) { | ||||||
|  | 				cursor.close(); // 关闭游标,避免内存泄漏 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return -1L; // 无匹配联系人 | ||||||
|  | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,9 +13,8 @@ import android.content.Context; | |||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.widget.RemoteViews; | import android.widget.RemoteViews; | ||||||
| import cc.winboll.studio.contacts.R; | import cc.winboll.studio.contacts.R; | ||||||
| import cc.winboll.studio.contacts.threads.MainServiceThread; |  | ||||||
| import cc.winboll.studio.libappbase.LogUtils; | import cc.winboll.studio.libappbase.LogUtils; | ||||||
| import com.hjq.toast.ToastUtils; | import cc.winboll.studio.libappbase.utils.ToastUtils; | ||||||
|  |  | ||||||
| public class APPStatusWidget extends AppWidgetProvider { | public class APPStatusWidget extends AppWidgetProvider { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,5 +5,8 @@ | |||||||
|     <item |     <item | ||||||
|         android:id="@+id/item_calllog_phonenumber_copy" |         android:id="@+id/item_calllog_phonenumber_copy" | ||||||
|         android:title="Copy"/> |         android:title="Copy"/> | ||||||
|  |     <item | ||||||
|  |         android:id="@+id/item_calllog_phonenumber_add_contact" | ||||||
|  |         android:title="Add Contact"/> | ||||||
|      |      | ||||||
| </menu> | </menu> | ||||||
|   | |||||||
| @@ -5,5 +5,8 @@ | |||||||
|     <item |     <item | ||||||
|         android:id="@+id/item_contact_phonenumber_copy" |         android:id="@+id/item_contact_phonenumber_copy" | ||||||
|         android:title="Copy"/> |         android:title="Copy"/> | ||||||
|  |     <item | ||||||
|  |         android:id="@+id/item_calllog_phonenumber_edit_contact" | ||||||
|  |         android:title="Edit Contact"/> | ||||||
|      |      | ||||||
| </menu> | </menu> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user