Compare commits
	
		
			2 Commits
		
	
	
		
			powerbell-
			...
			contacts-v
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| eb8d37c340 | |||
| 
						 | 
					d65a839878 | 
@@ -1,8 +1,8 @@
 | 
				
			|||||||
#Created by .winboll/winboll_app_build.gradle
 | 
					#Created by .winboll/winboll_app_build.gradle
 | 
				
			||||||
#Sat Sep 27 21:03:20 HKT 2025
 | 
					#Fri Sep 26 05:36:14 HKT 2025
 | 
				
			||||||
stageCount=10
 | 
					stageCount=9
 | 
				
			||||||
libraryProject=libappbase
 | 
					libraryProject=libappbase
 | 
				
			||||||
baseVersion=15.10
 | 
					baseVersion=15.10
 | 
				
			||||||
publishVersion=15.10.9
 | 
					publishVersion=15.10.8
 | 
				
			||||||
buildCount=0
 | 
					buildCount=0
 | 
				
			||||||
baseBetaVersion=15.10.10
 | 
					baseBetaVersion=15.10.9
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 Sep 27 15:23:51 HKT 2025
 | 
				
			||||||
stageCount=17
 | 
					stageCount=18
 | 
				
			||||||
libraryProject=
 | 
					libraryProject=
 | 
				
			||||||
baseVersion=15.3
 | 
					baseVersion=15.3
 | 
				
			||||||
publishVersion=15.3.16
 | 
					publishVersion=15.3.17
 | 
				
			||||||
buildCount=0
 | 
					buildCount=0
 | 
				
			||||||
baseBetaVersion=15.3.17
 | 
					baseBetaVersion=15.3.18
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
#Created by .winboll/winboll_app_build.gradle
 | 
					#Created by .winboll/winboll_app_build.gradle
 | 
				
			||||||
#Sat Sep 27 21:03:08 HKT 2025
 | 
					#Fri Sep 26 05:36:14 HKT 2025
 | 
				
			||||||
stageCount=10
 | 
					stageCount=9
 | 
				
			||||||
libraryProject=libappbase
 | 
					libraryProject=libappbase
 | 
				
			||||||
baseVersion=15.10
 | 
					baseVersion=15.10
 | 
				
			||||||
publishVersion=15.10.9
 | 
					publishVersion=15.10.8
 | 
				
			||||||
buildCount=0
 | 
					buildCount=0
 | 
				
			||||||
baseBetaVersion=15.10.10
 | 
					baseBetaVersion=15.10.9
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,12 +22,12 @@ public class GlobalApplication extends Application {
 | 
				
			|||||||
        GlobalApplication.isDebuging = isDebuging;
 | 
					        GlobalApplication.isDebuging = isDebuging;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public static void saveDebugStatus(Context context) {
 | 
					    public static void saveDebugStatus(GlobalApplication application) {
 | 
				
			||||||
        APPModel.saveBeanToFile(getAPPModelFilePath(context), new APPModel(GlobalApplication.isDebuging));
 | 
					        APPModel.saveBeanToFile(application.getAPPModelFilePath(application), new APPModel(GlobalApplication.isDebuging));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
    static String getAPPModelFilePath(Context context) {
 | 
					    static String getAPPModelFilePath(GlobalApplication application) {
 | 
				
			||||||
        return context.getDataDir().getPath() + "/APPModel.json";
 | 
					        return application.getDataDir().getPath() + "/APPModel.json";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static boolean isDebuging() {
 | 
					    public static boolean isDebuging() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,17 +41,6 @@ android {
 | 
				
			|||||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
					            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	// 允许使用系统隐藏API
 | 
					 | 
				
			||||||
    lintOptions {
 | 
					 | 
				
			||||||
        checkReleaseBuilds false
 | 
					 | 
				
			||||||
        abortOnError false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // 针对PowerProfile的依赖配置
 | 
					 | 
				
			||||||
    dependenciesInfo {
 | 
					 | 
				
			||||||
        includeInApk = false
 | 
					 | 
				
			||||||
        includeInBundle = false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
#Created by .winboll/winboll_app_build.gradle
 | 
					#Created by .winboll/winboll_app_build.gradle
 | 
				
			||||||
#Wed Oct 22 20:17:00 HKT 2025
 | 
					#Wed Sep 03 20:59:53 HKT 2025
 | 
				
			||||||
stageCount=18
 | 
					stageCount=13
 | 
				
			||||||
libraryProject=
 | 
					libraryProject=
 | 
				
			||||||
baseVersion=15.4
 | 
					baseVersion=15.4
 | 
				
			||||||
publishVersion=15.4.17
 | 
					publishVersion=15.4.12
 | 
				
			||||||
buildCount=0
 | 
					buildCount=0
 | 
				
			||||||
baseBetaVersion=15.4.18
 | 
					baseBetaVersion=15.4.13
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
<?xml version='1.0' encoding='utf-8'?>
 | 
					<?xml version='1.0' encoding='utf-8'?>
 | 
				
			||||||
<manifest
 | 
					<manifest
 | 
				
			||||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
	xmlns:tools="http://schemas.android.com/tools"
 | 
					 | 
				
			||||||
    package="cc.winboll.studio.powerbell">
 | 
					    package="cc.winboll.studio.powerbell">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- 拍摄照片和视频 -->
 | 
					    <!-- 拍摄照片和视频 -->
 | 
				
			||||||
@@ -25,29 +24,10 @@
 | 
				
			|||||||
    <!-- 显示通知 -->
 | 
					    <!-- 显示通知 -->
 | 
				
			||||||
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 | 
					    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- PACKAGE_USAGE_STATS -->
 | 
					 | 
				
			||||||
    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <!-- BATTERY_STATS -->
 | 
					 | 
				
			||||||
    <uses-permission android:name="android.permission.BATTERY_STATS"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <uses-feature android:name="android.hardware.camera"/>
 | 
					    <uses-feature android:name="android.hardware.camera"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <uses-feature android:name="android.hardware.camera.autofocus"/>
 | 
					    <uses-feature android:name="android.hardware.camera.autofocus"/>
 | 
				
			||||||
		
 | 
					 | 
				
			||||||
	<!-- 1. 基础应用信息读取权限(Android 11 及以下) -->
 | 
					 | 
				
			||||||
	<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<!-- 2. Android 11+ 应用列表读取权限(必须声明,否则无法获取全部应用) -->
 | 
					 | 
				
			||||||
	<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
 | 
					 | 
				
			||||||
		tools:ignore="QueryAllPackagesPermission" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<!-- 3. 可选:若需读取系统应用,添加此权限(部分机型需要) -->
 | 
					 | 
				
			||||||
	<uses-permission android:name="android.permission.ACCESS_PACKAGE_USAGE_STATS"
 | 
					 | 
				
			||||||
		tools:ignore="ProtectedPermissions" />
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
    <application
 | 
					    <application
 | 
				
			||||||
        android:name=".App"
 | 
					        android:name=".App"
 | 
				
			||||||
        android:allowBackup="true"
 | 
					        android:allowBackup="true"
 | 
				
			||||||
@@ -143,8 +123,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <activity android:name="cc.winboll.studio.powerbell.activities.PixelPickerActivity"/>
 | 
					        <activity android:name="cc.winboll.studio.powerbell.activities.PixelPickerActivity"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <activity android:name="cc.winboll.studio.powerbell.activities.BatteryReportActivity"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    </application>
 | 
					    </application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</manifest>
 | 
					</manifest>
 | 
				
			||||||
@@ -17,7 +17,7 @@ import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			|||||||
import cc.winboll.studio.powerbell.MainActivity;
 | 
					import cc.winboll.studio.powerbell.MainActivity;
 | 
				
			||||||
import cc.winboll.studio.powerbell.activities.AboutActivity;
 | 
					import cc.winboll.studio.powerbell.activities.AboutActivity;
 | 
				
			||||||
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
 | 
					import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
 | 
				
			||||||
import cc.winboll.studio.powerbell.activities.BatteryReportActivity;
 | 
					import cc.winboll.studio.powerbell.activities.BatteryReporterActivity;
 | 
				
			||||||
import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
 | 
					import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
 | 
				
			||||||
import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
 | 
					import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
 | 
				
			||||||
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
 | 
					import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
 | 
				
			||||||
@@ -159,9 +159,9 @@ public class MainActivity extends WinBoLLActivity {
 | 
				
			|||||||
        if (menuItemId == R.id.action_about) {
 | 
					        if (menuItemId == R.id.action_about) {
 | 
				
			||||||
            Intent intent = new Intent(this, AboutActivity.class);
 | 
					            Intent intent = new Intent(this, AboutActivity.class);
 | 
				
			||||||
            startActivity(intent);
 | 
					            startActivity(intent);
 | 
				
			||||||
        } else if (menuItemId == R.id.action_battery_report) {
 | 
					        } else if (menuItemId == R.id.action_battery_reporter) {
 | 
				
			||||||
            Intent intent = new Intent();
 | 
					            Intent intent = new Intent();
 | 
				
			||||||
            intent.setClass(this, BatteryReportActivity.class);
 | 
					            intent.setClass(this, BatteryReporterActivity.class);
 | 
				
			||||||
            startActivity(intent);
 | 
					            startActivity(intent);
 | 
				
			||||||
        } else if (menuItemId == R.id.action_clearrecord) {
 | 
					        } else if (menuItemId == R.id.action_clearrecord) {
 | 
				
			||||||
            Intent intent = new Intent();
 | 
					            Intent intent = new Intent();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,516 +0,0 @@
 | 
				
			|||||||
package cc.winboll.studio.powerbell.activities;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
 | 
					 | 
				
			||||||
 * @Date 2025/10/22 13:21
 | 
					 | 
				
			||||||
 * @Describe BatteryReportActivity
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
import android.app.Activity;
 | 
					 | 
				
			||||||
import android.content.BroadcastReceiver;
 | 
					 | 
				
			||||||
import android.content.Context;
 | 
					 | 
				
			||||||
import android.content.Intent;
 | 
					 | 
				
			||||||
import android.content.IntentFilter;
 | 
					 | 
				
			||||||
import android.content.pm.ApplicationInfo;
 | 
					 | 
				
			||||||
import android.content.pm.PackageManager;
 | 
					 | 
				
			||||||
import android.os.Build;
 | 
					 | 
				
			||||||
import android.os.Bundle;
 | 
					 | 
				
			||||||
import android.provider.Settings;
 | 
					 | 
				
			||||||
import android.text.Editable;
 | 
					 | 
				
			||||||
import android.text.TextWatcher;
 | 
					 | 
				
			||||||
import android.view.LayoutInflater;
 | 
					 | 
				
			||||||
import android.view.View;
 | 
					 | 
				
			||||||
import android.view.ViewGroup;
 | 
					 | 
				
			||||||
import android.widget.EditText;
 | 
					 | 
				
			||||||
import android.widget.TextView;
 | 
					 | 
				
			||||||
import android.widget.Toast;
 | 
					 | 
				
			||||||
import androidx.recyclerview.widget.LinearLayoutManager;
 | 
					 | 
				
			||||||
import androidx.recyclerview.widget.RecyclerView;
 | 
					 | 
				
			||||||
import cc.winboll.studio.powerbell.R;
 | 
					 | 
				
			||||||
import java.util.ArrayList;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					 | 
				
			||||||
import java.util.Comparator;
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
import cc.winboll.studio.libappbase.LogUtils;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class BatteryReportActivity extends Activity {
 | 
					 | 
				
			||||||
    public static final String TAG = "BatteryReportActivity";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private RecyclerView rvBatteryReport;
 | 
					 | 
				
			||||||
    private BatteryReportAdapter adapter;
 | 
					 | 
				
			||||||
    private List<AppBatteryModel> dataList = new ArrayList<AppBatteryModel>(); 
 | 
					 | 
				
			||||||
    private List<AppBatteryModel> filteredList = new ArrayList<AppBatteryModel>(); 
 | 
					 | 
				
			||||||
    private BroadcastReceiver batteryReceiver;
 | 
					 | 
				
			||||||
    private int batteryCapacity = 5400; // 电池容量(mAh)
 | 
					 | 
				
			||||||
    private float lastBatteryPercent = 100.0f;
 | 
					 | 
				
			||||||
    private long lastCheckTime = System.currentTimeMillis();
 | 
					 | 
				
			||||||
    private EditText etSearch;
 | 
					 | 
				
			||||||
    private Map<String, Long> appRunTimeCache = new HashMap<String, Long>();
 | 
					 | 
				
			||||||
    private Map<String, String> packageToAppNameCache = new HashMap<String, String>();
 | 
					 | 
				
			||||||
    private PackageManager mPackageManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
					 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					 | 
				
			||||||
        setContentView(R.layout.activity_battery_report);
 | 
					 | 
				
			||||||
        mPackageManager = getPackageManager();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 权限检查(Java7 传统条件判断)
 | 
					 | 
				
			||||||
        if (!hasUsageStatsPermission(this)) {
 | 
					 | 
				
			||||||
            Toast.makeText(this, "请进入设置-应用-权限-特殊访问权限-使用情况访问权限,开启本应用的权限", Toast.LENGTH_LONG).show();
 | 
					 | 
				
			||||||
            startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        etSearch = (EditText) findViewById(R.id.et_search); 
 | 
					 | 
				
			||||||
        rvBatteryReport = (RecyclerView) findViewById(R.id.rv_battery_report); 
 | 
					 | 
				
			||||||
        rvBatteryReport.setLayoutManager(new LinearLayoutManager(this));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 初始化流程:新增“加载24小时累计耗电”步骤
 | 
					 | 
				
			||||||
        loadAllAppPackage();
 | 
					 | 
				
			||||||
        preCacheAllAppNames();
 | 
					 | 
				
			||||||
        appRunTimeCache = getAppRunTime();
 | 
					 | 
				
			||||||
        updateAppRunTimeToModel();
 | 
					 | 
				
			||||||
        calculateInitial24hTotalConsumption(); // 初始化时计算24小时累计耗电
 | 
					 | 
				
			||||||
        filteredList.addAll(dataList);
 | 
					 | 
				
			||||||
        adapter = new BatteryReportAdapter(this, filteredList, mPackageManager, packageToAppNameCache);
 | 
					 | 
				
			||||||
        rvBatteryReport.setAdapter(adapter);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 搜索监听(不变)
 | 
					 | 
				
			||||||
        etSearch.addTextChangedListener(new TextWatcher() {
 | 
					 | 
				
			||||||
				@Override
 | 
					 | 
				
			||||||
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				@Override
 | 
					 | 
				
			||||||
				public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
					 | 
				
			||||||
					filterAppsByPackageAndName(s.toString());
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				@Override
 | 
					 | 
				
			||||||
				public void afterTextChanged(Editable s) {}
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 电池广播:调用修改后的“单次耗电计算+累计累加”方法
 | 
					 | 
				
			||||||
        batteryReceiver = new BroadcastReceiver() {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void onReceive(Context context, Intent intent) {
 | 
					 | 
				
			||||||
                int level = intent.getIntExtra("level", 100);
 | 
					 | 
				
			||||||
                int scale = intent.getIntExtra("scale", 100);
 | 
					 | 
				
			||||||
                float currentPercent = (float) level / scale * 100;
 | 
					 | 
				
			||||||
                LogUtils.d(TAG, "电池百分比变化:" + lastBatteryPercent + " -> " + currentPercent);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (currentPercent < lastBatteryPercent) {
 | 
					 | 
				
			||||||
                    float dropPercent = lastBatteryPercent - currentPercent;
 | 
					 | 
				
			||||||
                    long duration = System.currentTimeMillis() - lastCheckTime;
 | 
					 | 
				
			||||||
                    LogUtils.d(TAG, "电池消耗:" + dropPercent + "%,时长:" + duration + "ms");
 | 
					 | 
				
			||||||
                    appRunTimeCache = getAppRunTime();
 | 
					 | 
				
			||||||
                    updateAppRunTimeToModel();
 | 
					 | 
				
			||||||
                    calculateSingleConsumptionAndAccumulate(dropPercent, appRunTimeCache); // 单次+累计逻辑
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                lastBatteryPercent = currentPercent;
 | 
					 | 
				
			||||||
                lastCheckTime = System.currentTimeMillis();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onDestroy() {
 | 
					 | 
				
			||||||
        super.onDestroy();
 | 
					 | 
				
			||||||
        // Java7 显式非空判断
 | 
					 | 
				
			||||||
        if (batteryReceiver != null) {
 | 
					 | 
				
			||||||
            unregisterReceiver(batteryReceiver);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 加载所有应用(仅获取包名,初始化模型时单次耗电、累计耗电均设为0)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private void loadAllAppPackage() {
 | 
					 | 
				
			||||||
        List<ApplicationInfo> appList = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA);
 | 
					 | 
				
			||||||
        dataList.clear();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, "开始加载应用包名列表,共找到" + appList.size() + "个应用");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (ApplicationInfo appInfo : appList) {
 | 
					 | 
				
			||||||
            String packageName = appInfo.packageName;
 | 
					 | 
				
			||||||
            // 初始化:单次耗电(consumption)=0,累计耗电(totalConsumption)=0,运行时长=0
 | 
					 | 
				
			||||||
            dataList.add(new AppBatteryModel(packageName, 0.0f, 0.0f, 0));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, "应用包名列表加载完成,共添加" + dataList.size() + "个包名。");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 预缓存应用名称(逻辑不变)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private void preCacheAllAppNames() {
 | 
					 | 
				
			||||||
        packageToAppNameCache.clear();
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, "开始预缓存包名-应用名称映射");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (AppBatteryModel model : dataList) {
 | 
					 | 
				
			||||||
            String packageName = model.getPackageName();
 | 
					 | 
				
			||||||
            String appName = getAppNameByPackage(packageName);
 | 
					 | 
				
			||||||
            packageToAppNameCache.put(packageName, appName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, "预缓存完成,共缓存" + packageToAppNameCache.size() + "个应用名称");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 通过包名获取应用名称(逻辑不变)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private String getAppNameByPackage(String packageName) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
 | 
					 | 
				
			||||||
            return mPackageManager.getApplicationLabel(appInfo).toString();
 | 
					 | 
				
			||||||
        } catch (PackageManager.NameNotFoundException e) {
 | 
					 | 
				
			||||||
            LogUtils.e(TAG, "包名" + packageName + "对应的应用未找到:" + e.getMessage());
 | 
					 | 
				
			||||||
            return packageName;
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            LogUtils.e(TAG, "查询应用名称失败(包名:" + packageName + "):" + e.getMessage());
 | 
					 | 
				
			||||||
            return packageName;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 更新运行时长到模型(逻辑不变)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private void updateAppRunTimeToModel() {
 | 
					 | 
				
			||||||
        int nCount = 0;
 | 
					 | 
				
			||||||
        for (AppBatteryModel model : dataList) {
 | 
					 | 
				
			||||||
            String packageName = model.getPackageName();
 | 
					 | 
				
			||||||
            Long runTime;
 | 
					 | 
				
			||||||
            if (appRunTimeCache.containsKey(packageName)) {
 | 
					 | 
				
			||||||
                runTime = appRunTimeCache.get(packageName);
 | 
					 | 
				
			||||||
                LogUtils.d(TAG, String.format("应用包 %s 运行时长已更新。", packageName));
 | 
					 | 
				
			||||||
                nCount++;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                runTime = 0L;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            model.setRunTime(runTime);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, String.format("dataList.size() %d, appRunTimeCache.size() %d。", dataList.size(), appRunTimeCache.size()));
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, String.format("updateAppRunTimeToModel() 更新的数据量为:%d", nCount));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 【新增】初始化时计算24小时累计耗电(赋值给totalConsumption)
 | 
					 | 
				
			||||||
     * 逻辑:基于24小时运行时长占比,分配当前电池容量的理论24小时消耗
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private void calculateInitial24hTotalConsumption() {
 | 
					 | 
				
			||||||
        long total24hRunTime = 0;
 | 
					 | 
				
			||||||
        // 1. 计算24小时内所有应用总运行时长
 | 
					 | 
				
			||||||
        for (Map.Entry<String, Long> entry : appRunTimeCache.entrySet()) {
 | 
					 | 
				
			||||||
            total24hRunTime += entry.getValue();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, "24小时内所有应用总运行时长:" + formatRunTime(total24hRunTime));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 2. 按运行时长占比分配24小时累计耗电(假设电池满电循环,用总容量近似24小时总消耗)
 | 
					 | 
				
			||||||
        for (AppBatteryModel model : dataList) {
 | 
					 | 
				
			||||||
            String packageName = model.getPackageName();
 | 
					 | 
				
			||||||
            Long app24hRunTime = appRunTimeCache.getOrDefault(packageName, 0L);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 计算占比与累计耗电
 | 
					 | 
				
			||||||
            float ratio = (total24hRunTime > 0) ? (float) app24hRunTime / total24hRunTime : 0;
 | 
					 | 
				
			||||||
            float initialTotalConsumption = batteryCapacity * ratio; // 用电池容量近似24小时总消耗
 | 
					 | 
				
			||||||
            model.setTotalConsumption(initialTotalConsumption); // 初始化累计耗电
 | 
					 | 
				
			||||||
            LogUtils.d(TAG, String.format("应用包 %s 24小时累计耗电初始化:%.1f mAh", packageName, initialTotalConsumption));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 【核心修改】计算单次耗电(赋值给consumption)+ 累加至累计耗电(totalConsumption = totalConsumption + consumption)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private void calculateSingleConsumptionAndAccumulate(float dropPercent, Map<String, Long> runTimeMap) {
 | 
					 | 
				
			||||||
        long totalSingleRunTime = 0;
 | 
					 | 
				
			||||||
        // 1. 计算本次电池下降期间的总运行时长
 | 
					 | 
				
			||||||
        for (Map.Entry<String, Long> entry : runTimeMap.entrySet()) {
 | 
					 | 
				
			||||||
            totalSingleRunTime += entry.getValue();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 2. 遍历计算每个应用的“单次耗电”并“累加至累计”
 | 
					 | 
				
			||||||
        for (AppBatteryModel model : dataList) {
 | 
					 | 
				
			||||||
            String packageName = model.getPackageName();
 | 
					 | 
				
			||||||
            Long appSingleRunTime = runTimeMap.getOrDefault(packageName, 0L);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 步骤1:计算本次单次耗电(赋值给consumption)
 | 
					 | 
				
			||||||
            float ratio = (totalSingleRunTime > 0) ? (float) appSingleRunTime / totalSingleRunTime : 0;
 | 
					 | 
				
			||||||
            float singleConsumption = batteryCapacity * dropPercent / 100 * ratio; // 单次消耗
 | 
					 | 
				
			||||||
            model.setConsumption(singleConsumption); // 存储单次耗电
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 步骤2:累加单次耗电到累计耗电(totalConsumption = 原有累计 + 本次单次)
 | 
					 | 
				
			||||||
            float newTotalConsumption = model.getTotalConsumption() + singleConsumption;
 | 
					 | 
				
			||||||
            model.setTotalConsumption(newTotalConsumption); // 更新累计耗电
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 同步运行时长
 | 
					 | 
				
			||||||
            model.setRunTime(appSingleRunTime);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            LogUtils.d(TAG, String.format("应用包 %s:单次耗电%.1f mAh,累计耗电%.1f mAh", 
 | 
					 | 
				
			||||||
										  packageName, singleConsumption, newTotalConsumption));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 3. 按累计耗电排序(从高到低)
 | 
					 | 
				
			||||||
        Collections.sort(dataList, new Comparator<AppBatteryModel>() {
 | 
					 | 
				
			||||||
				@Override
 | 
					 | 
				
			||||||
				public int compare(AppBatteryModel m1, AppBatteryModel m2) {
 | 
					 | 
				
			||||||
					return Float.compare(m2.getTotalConsumption(), m1.getTotalConsumption());
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 4. 重新应用过滤并刷新列表
 | 
					 | 
				
			||||||
        filterAppsByPackageAndName(etSearch.getText().toString());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 双维度过滤(逻辑不变)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private void filterAppsByPackageAndName(String keyword) {
 | 
					 | 
				
			||||||
        filteredList.clear();
 | 
					 | 
				
			||||||
        if (keyword == null || keyword.isEmpty()) {
 | 
					 | 
				
			||||||
            filteredList.addAll(dataList);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            String lowerKeyword = keyword.toLowerCase();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (AppBatteryModel model : dataList) {
 | 
					 | 
				
			||||||
                String packageName = model.getPackageName();
 | 
					 | 
				
			||||||
                String packageNameLower = packageName.toLowerCase();
 | 
					 | 
				
			||||||
                String appName = packageToAppNameCache.get(packageName);
 | 
					 | 
				
			||||||
                String appNameLower = appName.toLowerCase();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                boolean isMatched = packageNameLower.contains(lowerKeyword) 
 | 
					 | 
				
			||||||
                    || appNameLower.contains(lowerKeyword);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (isMatched) {
 | 
					 | 
				
			||||||
                    filteredList.add(model);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        adapter.notifyDataSetChanged();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 获取应用运行时长(逻辑不变,返回24小时运行时长)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private Map<String, Long> getAppRunTime() {
 | 
					 | 
				
			||||||
        Map<String, Long> runTimeMap = new HashMap<String, Long>();
 | 
					 | 
				
			||||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                android.app.usage.UsageStatsManager manager =
 | 
					 | 
				
			||||||
                    (android.app.usage.UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
 | 
					 | 
				
			||||||
                long endTime = System.currentTimeMillis();
 | 
					 | 
				
			||||||
                long startTime = endTime - 24 * 3600 * 1000; // 近24小时
 | 
					 | 
				
			||||||
                List<android.app.usage.UsageStats> statsList = manager.queryUsageStats(
 | 
					 | 
				
			||||||
                    android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                for (android.app.usage.UsageStats stats : statsList) {
 | 
					 | 
				
			||||||
                    long runTimeMs = stats.getTotalTimeInForeground();
 | 
					 | 
				
			||||||
                    String packageName = stats.getPackageName();
 | 
					 | 
				
			||||||
                    LogUtils.d(TAG, "包名" + packageName + "24小时运行时长:" + formatRunTime(runTimeMs));
 | 
					 | 
				
			||||||
                    runTimeMap.put(packageName, runTimeMs);
 | 
					 | 
				
			||||||
                    if (packageName.equals("aidepro.top")) {
 | 
					 | 
				
			||||||
                        LogUtils.d(TAG, String.format("runTimeMap.put(packageName, runTimeMs) 特殊查询 %s 查询有结果。", packageName));
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } catch (Exception e) {
 | 
					 | 
				
			||||||
                LogUtils.e(TAG, "获取应用运行时长失败:" + e.getMessage());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LogUtils.d(TAG, String.format("应用运行时长列表数量%d。", runTimeMap.size()));
 | 
					 | 
				
			||||||
        return runTimeMap;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 格式化运行时长(逻辑不变)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private String formatRunTime(long runTimeMs) {
 | 
					 | 
				
			||||||
        if (runTimeMs <= 0) {
 | 
					 | 
				
			||||||
            return "0秒";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        long seconds = runTimeMs / 1000;
 | 
					 | 
				
			||||||
        long hours = seconds / 3600;
 | 
					 | 
				
			||||||
        long minutes = (seconds % 3600) / 60;
 | 
					 | 
				
			||||||
        seconds = seconds % 60;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (hours > 0) {
 | 
					 | 
				
			||||||
            return String.format("%d时%d分%d秒", hours, minutes, seconds);
 | 
					 | 
				
			||||||
        } else if (minutes > 0) {
 | 
					 | 
				
			||||||
            return String.format("%d分%d秒", minutes, seconds);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            return String.format("%d秒", seconds);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 权限检查(逻辑不变)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private boolean hasUsageStatsPermission(Context context) {
 | 
					 | 
				
			||||||
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        android.app.usage.UsageStatsManager manager =
 | 
					 | 
				
			||||||
            (android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
 | 
					 | 
				
			||||||
        if (manager == null) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        long endTime = System.currentTimeMillis();
 | 
					 | 
				
			||||||
        long startTime = endTime - 1000 * 60;
 | 
					 | 
				
			||||||
        List<android.app.usage.UsageStats> statsList = manager.queryUsageStats(
 | 
					 | 
				
			||||||
            android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return statsList != null && !statsList.isEmpty();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 【核心修改】数据模型:明确字段含义
 | 
					 | 
				
			||||||
     * - consumption:单次耗电(两次电池广播间的消耗,float类型便于计算)
 | 
					 | 
				
			||||||
     * - totalConsumption:累计耗电(24小时初始化值+后续单次累加,显示用)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static class AppBatteryModel {
 | 
					 | 
				
			||||||
        private String packageName;    // 应用包名(核心标识)
 | 
					 | 
				
			||||||
        private float consumption;     // 单次耗电(mAh,float类型)
 | 
					 | 
				
			||||||
        private float totalConsumption;// 累计耗电(mAh,显示+排序用)
 | 
					 | 
				
			||||||
        private long runTime;          // 运行时长(ms)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Java7 显式构造:初始化单次耗电、累计耗电为0
 | 
					 | 
				
			||||||
        public AppBatteryModel(String packageName, float consumption, float totalConsumption, long runTime) {
 | 
					 | 
				
			||||||
            this.packageName = packageName;
 | 
					 | 
				
			||||||
            this.consumption = consumption; // 单次耗电初始为0
 | 
					 | 
				
			||||||
            this.totalConsumption = totalConsumption; // 累计耗电初始为0(后续初始化时赋值)
 | 
					 | 
				
			||||||
            this.runTime = runTime;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Getter/Setter:覆盖所有字段,确保数据操作正常
 | 
					 | 
				
			||||||
        public String getPackageName() {
 | 
					 | 
				
			||||||
            return packageName;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public float getConsumption() {
 | 
					 | 
				
			||||||
            return consumption; // 获取单次耗电
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void setConsumption(float consumption) {
 | 
					 | 
				
			||||||
            this.consumption = consumption; // 设置单次耗电
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public float getTotalConsumption() {
 | 
					 | 
				
			||||||
            return totalConsumption; // 获取累计耗电(显示用)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void setTotalConsumption(float totalConsumption) {
 | 
					 | 
				
			||||||
            this.totalConsumption = totalConsumption; // 设置累计耗电(初始化/累加用)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public long getRunTime() {
 | 
					 | 
				
			||||||
            return runTime;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void setRunTime(long runTime) {
 | 
					 | 
				
			||||||
            this.runTime = runTime;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * RecyclerView 适配器:仅显示累计耗电(totalConsumption),逻辑适配模型修改
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static class BatteryReportAdapter extends RecyclerView.Adapter<BatteryReportAdapter.ViewHolder> {
 | 
					 | 
				
			||||||
        private Context mContext;
 | 
					 | 
				
			||||||
        private List<AppBatteryModel> mDataList;
 | 
					 | 
				
			||||||
        private PackageManager mPm;
 | 
					 | 
				
			||||||
        private Map<String, String> mPackageToNameCache;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Java7 显式构造:接收名称缓存,确保显示时高效获取应用名
 | 
					 | 
				
			||||||
        public BatteryReportAdapter(Context context, List<AppBatteryModel> dataList, 
 | 
					 | 
				
			||||||
                                    PackageManager pm, Map<String, String> packageToNameCache) {
 | 
					 | 
				
			||||||
            this.mContext = context;
 | 
					 | 
				
			||||||
            this.mDataList = dataList;
 | 
					 | 
				
			||||||
            this.mPm = pm;
 | 
					 | 
				
			||||||
            this.mPackageToNameCache = packageToNameCache;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 | 
					 | 
				
			||||||
            // 加载系统列表项布局(text1显示应用名,text2显示累计耗电+时长)
 | 
					 | 
				
			||||||
            View itemView = LayoutInflater.from(mContext)
 | 
					 | 
				
			||||||
                .inflate(android.R.layout.simple_list_item_2, parent, false);
 | 
					 | 
				
			||||||
            return new ViewHolder(itemView);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onBindViewHolder(ViewHolder holder, int position) {
 | 
					 | 
				
			||||||
            // Java7 显式非空判断:避免空指针异常
 | 
					 | 
				
			||||||
            if (mDataList == null || mDataList.isEmpty() || position >= mDataList.size()) {
 | 
					 | 
				
			||||||
                holder.tvAppName.setText("未知应用");
 | 
					 | 
				
			||||||
                holder.tvConsumption.setText("累计耗电:0.0 mAh | 运行时长:0秒");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            AppBatteryModel model = mDataList.get(position);
 | 
					 | 
				
			||||||
            String packageName = model.getPackageName();
 | 
					 | 
				
			||||||
            String appName = "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 优先从缓存获取应用名:减少PackageManager调用,提升性能
 | 
					 | 
				
			||||||
            if (mPackageToNameCache != null && mPackageToNameCache.containsKey(packageName)) {
 | 
					 | 
				
			||||||
                appName = mPackageToNameCache.get(packageName);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // 缓存无数据时兜底查询,并同步更新缓存
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0);
 | 
					 | 
				
			||||||
                    appName = mPm.getApplicationLabel(appInfo).toString();
 | 
					 | 
				
			||||||
                    if (mPackageToNameCache != null) {
 | 
					 | 
				
			||||||
                        mPackageToNameCache.put(packageName, appName);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } catch (PackageManager.NameNotFoundException e) {
 | 
					 | 
				
			||||||
                    appName = packageName; // 包名不存在时用包名兜底
 | 
					 | 
				
			||||||
                    LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage());
 | 
					 | 
				
			||||||
                } catch (Exception e) {
 | 
					 | 
				
			||||||
                    appName = packageName; // 其他异常时用包名兜底
 | 
					 | 
				
			||||||
                    LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 显示逻辑:仅展示累计耗电(totalConsumption),隐藏单次耗电
 | 
					 | 
				
			||||||
            holder.tvAppName.setText(appName);
 | 
					 | 
				
			||||||
            // 格式化运行时长 + 累计耗电(保留1位小数,提升可读性)
 | 
					 | 
				
			||||||
            String runTimeStr = ((BatteryReportActivity) mContext).formatRunTime(model.getRunTime());
 | 
					 | 
				
			||||||
            String totalConsumptionText = String.format("累计耗电:%.1f mAh | 运行时长:%s",
 | 
					 | 
				
			||||||
														model.getTotalConsumption(), runTimeStr);
 | 
					 | 
				
			||||||
            holder.tvConsumption.setText(totalConsumptionText);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 显示优化:文字颜色区分(避免所有应用均标蓝,仅示例可按需修改)
 | 
					 | 
				
			||||||
            holder.tvAppName.setTextColor(mContext.getResources().getColor(android.R.color.black));
 | 
					 | 
				
			||||||
            holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.darker_gray));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 调整文字大小:适配手机屏幕,提升可读性
 | 
					 | 
				
			||||||
            holder.tvAppName.setTextSize(16);
 | 
					 | 
				
			||||||
            holder.tvConsumption.setTextSize(14);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 获取列表长度:Java7 三元运算符判断空值,避免空指针
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public int getItemCount() {
 | 
					 | 
				
			||||||
            return mDataList == null ? 0 : mDataList.size();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * ViewHolder:绑定系统布局控件,与显示逻辑对应
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        public static class ViewHolder extends RecyclerView.ViewHolder {
 | 
					 | 
				
			||||||
            TextView tvAppName;     // 显示应用名称
 | 
					 | 
				
			||||||
            TextView tvConsumption; // 显示累计耗电 + 运行时长
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Java7 显式构造:绑定控件ID(系统布局固定ID:text1、text2)
 | 
					 | 
				
			||||||
            public ViewHolder(View itemView) {
 | 
					 | 
				
			||||||
                super(itemView);
 | 
					 | 
				
			||||||
                tvAppName = (TextView) itemView.findViewById(android.R.id.text1);
 | 
					 | 
				
			||||||
                tvConsumption = (TextView) itemView.findViewById(android.R.id.text2);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.powerbell.activities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen<zhangsken@qq.com>
 | 
				
			||||||
 | 
					 * @Date 2025/03/22 14:20:15
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.app.Activity;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.DividerItemDecoration;
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.LinearLayoutManager;
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.RecyclerView;
 | 
				
			||||||
 | 
					import cc.winboll.studio.powerbell.R;
 | 
				
			||||||
 | 
					import cc.winboll.studio.powerbell.adapters.BatteryAdapter;
 | 
				
			||||||
 | 
					import cc.winboll.studio.powerbell.beans.BatteryData;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class BatteryReporterActivity extends Activity {
 | 
				
			||||||
 | 
					    public static final String TAG = "BatteryReporterActivity";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private RecyclerView rvBatteryReport;
 | 
				
			||||||
 | 
					    private BatteryAdapter adapter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_battery_reporter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rvBatteryReport = findViewById(R.id.rvBatteryReport);
 | 
				
			||||||
 | 
					        setupRecyclerView();
 | 
				
			||||||
 | 
					        loadSampleData();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void setupRecyclerView() {
 | 
				
			||||||
 | 
					        adapter = new BatteryAdapter();
 | 
				
			||||||
 | 
					        rvBatteryReport.setLayoutManager(new LinearLayoutManager(this));
 | 
				
			||||||
 | 
					        rvBatteryReport.setAdapter(adapter);
 | 
				
			||||||
 | 
					        rvBatteryReport.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void loadSampleData() {
 | 
				
			||||||
 | 
					        List<BatteryData> dataList = Arrays.asList(
 | 
				
			||||||
 | 
					            new BatteryData(95, "01:23:45", "00:05:12"),
 | 
				
			||||||
 | 
					            new BatteryData(80, "02:15:30", "00:10:00"),
 | 
				
			||||||
 | 
					            new BatteryData(65, "03:45:15", "00:15:30"),
 | 
				
			||||||
 | 
					            new BatteryData(50, "05:00:00", "00:20:45")
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        adapter.updateData(dataList);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<LinearLayout 
 | 
					 | 
				
			||||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					 | 
				
			||||||
    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
    android:layout_height="match_parent"
 | 
					 | 
				
			||||||
    android:orientation="vertical"
 | 
					 | 
				
			||||||
    android:background="@android:color/white">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <!-- 搜索框:提示文本改为“搜索应用名称或包名” -->
 | 
					 | 
				
			||||||
    <EditText
 | 
					 | 
				
			||||||
        android:id="@+id/et_search"
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
        android:layout_margin="8dp"
 | 
					 | 
				
			||||||
        android:padding="12dp"
 | 
					 | 
				
			||||||
        android:hint="搜索应用名称或包名"
 | 
					 | 
				
			||||||
        android:background="@android:drawable/btn_default_small"
 | 
					 | 
				
			||||||
        android:inputType="text"
 | 
					 | 
				
			||||||
        android:textSize="16sp"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <!-- 应用列表 -->
 | 
					 | 
				
			||||||
    <androidx.recyclerview.widget.RecyclerView
 | 
					 | 
				
			||||||
        android:id="@+id/rv_battery_report"
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="0dp"
 | 
					 | 
				
			||||||
        android:layout_weight="1"
 | 
					 | 
				
			||||||
        android:layout_marginLeft="8dp"
 | 
					 | 
				
			||||||
        android:layout_marginRight="8dp"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</LinearLayout>
 | 
					 | 
				
			||||||
							
								
								
									
										24
									
								
								powerbell/src/main/res/layout/activity_battery_reporter.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								powerbell/src/main/res/layout/activity_battery_reporter.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout 
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					    android:orientation="vertical"
 | 
				
			||||||
 | 
					    android:padding="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:text="电池使用报告"
 | 
				
			||||||
 | 
					        android:textSize="24sp"
 | 
				
			||||||
 | 
					        android:fontFamily="sans-serif-medium"
 | 
				
			||||||
 | 
					        android:layout_marginBottom="16dp"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					        android:id="@+id/rvBatteryReport"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
 | 
					        android:divider="@drawable/divider_line"
 | 
				
			||||||
 | 
					        android:dividerHeight="1dp"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
 | 
					<menu xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
    <item
 | 
					    <item
 | 
				
			||||||
        android:id="@+id/action_battery_report"
 | 
					        android:id="@+id/action_battery_reporter"
 | 
				
			||||||
        android:title="@string/item_battery_report"/>
 | 
					        android:title="@string/item_battery_reporter"/>
 | 
				
			||||||
    <item
 | 
					    <item
 | 
				
			||||||
        android:id="@+id/action_clearrecord"
 | 
					        android:id="@+id/action_clearrecord"
 | 
				
			||||||
        android:title="@string/item_clearrecord"/>
 | 
					        android:title="@string/item_clearrecord"/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
    <string name="about_crashed">This application has crashed, the author level is limited, please understand!</string>
 | 
					    <string name="about_crashed">This application has crashed, the author level is limited, please understand!</string>
 | 
				
			||||||
    <string name="item_mainview">Main View</string>
 | 
					    <string name="item_mainview">Main View</string>
 | 
				
			||||||
    <string name="item_aboutview">About</string>
 | 
					    <string name="item_aboutview">About</string>
 | 
				
			||||||
    <string name="item_battery_report">Battery Report</string>
 | 
					    <string name="item_battery_reporter">Battery Reporter</string>
 | 
				
			||||||
    <string name="item_clearrecord">Clear Record</string>
 | 
					    <string name="item_clearrecord">Clear Record</string>
 | 
				
			||||||
    <string name="item_changepicture">Change Picture</string>
 | 
					    <string name="item_changepicture">Change Picture</string>
 | 
				
			||||||
    <string name="item_devoloperoptionsview">Developer View</string>
 | 
					    <string name="item_devoloperoptionsview">Developer View</string>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user