Compare commits
	
		
			15 Commits
		
	
	
		
			aes-v15.9.
			...
			powerbell
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f943db17e0 | |||
|   | d7a9cb2a20 | ||
| de34c33706 | |||
|   | 10b8da2e21 | ||
|   | ca4e4c7feb | ||
| 4108371c20 | |||
|   | e5c8624d9b | ||
| 561330697b | |||
|   | f7b2c0d4c0 | ||
|   | c3978a1e3c | ||
| 5e198d9c68 | |||
|   | 963a3bb7cd | ||
| e9bb789daa | |||
| dbff19e7f4 | |||
|   | 44679d0c8a | 
| @@ -1,8 +1,8 @@ | |||||||
| #Created by .winboll/winboll_app_build.gradle | #Created by .winboll/winboll_app_build.gradle | ||||||
| #Fri Sep 26 05:36:14 HKT 2025 | #Sat Sep 27 21:03:20 HKT 2025 | ||||||
| stageCount=9 | stageCount=10 | ||||||
| libraryProject=libappbase | libraryProject=libappbase | ||||||
| baseVersion=15.10 | baseVersion=15.10 | ||||||
| publishVersion=15.10.8 | publishVersion=15.10.9 | ||||||
| buildCount=0 | buildCount=0 | ||||||
| baseBetaVersion=15.10.9 | baseBetaVersion=15.10.10 | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #Created by .winboll/winboll_app_build.gradle | #Created by .winboll/winboll_app_build.gradle | ||||||
| #Fri Sep 26 05:36:14 HKT 2025 | #Sat Sep 27 21:03:08 HKT 2025 | ||||||
| stageCount=9 | stageCount=10 | ||||||
| libraryProject=libappbase | libraryProject=libappbase | ||||||
| baseVersion=15.10 | baseVersion=15.10 | ||||||
| publishVersion=15.10.8 | publishVersion=15.10.9 | ||||||
| buildCount=0 | buildCount=0 | ||||||
| baseBetaVersion=15.10.9 | baseBetaVersion=15.10.10 | ||||||
|   | |||||||
| @@ -22,12 +22,12 @@ public class GlobalApplication extends Application { | |||||||
|         GlobalApplication.isDebuging = isDebuging; |         GlobalApplication.isDebuging = isDebuging; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     public static void saveDebugStatus(GlobalApplication application) { |     public static void saveDebugStatus(Context context) { | ||||||
|         APPModel.saveBeanToFile(application.getAPPModelFilePath(application), new APPModel(GlobalApplication.isDebuging)); |         APPModel.saveBeanToFile(getAPPModelFilePath(context), new APPModel(GlobalApplication.isDebuging)); | ||||||
|     } |     } | ||||||
| 	 | 	 | ||||||
|     static String getAPPModelFilePath(GlobalApplication application) { |     static String getAPPModelFilePath(Context context) { | ||||||
|         return application.getDataDir().getPath() + "/APPModel.json"; |         return context.getDataDir().getPath() + "/APPModel.json"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static boolean isDebuging() { |     public static boolean isDebuging() { | ||||||
|   | |||||||
| @@ -41,6 +41,17 @@ 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 Sep 03 20:59:53 HKT 2025 | #Wed Oct 22 20:17:00 HKT 2025 | ||||||
| stageCount=13 | stageCount=18 | ||||||
| libraryProject= | libraryProject= | ||||||
| baseVersion=15.4 | baseVersion=15.4 | ||||||
| publishVersion=15.4.12 | publishVersion=15.4.17 | ||||||
| buildCount=0 | buildCount=0 | ||||||
| baseBetaVersion=15.4.13 | baseBetaVersion=15.4.18 | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| <?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"> | ||||||
|  |  | ||||||
|     <!-- 拍摄照片和视频 --> |     <!-- 拍摄照片和视频 --> | ||||||
| @@ -24,10 +25,29 @@ | |||||||
|     <!-- 显示通知 --> |     <!-- 显示通知 --> | ||||||
|     <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" | ||||||
| @@ -123,6 +143,8 @@ | |||||||
|  |  | ||||||
|         <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.BatteryReporterActivity; | import cc.winboll.studio.powerbell.activities.BatteryReportActivity; | ||||||
| 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_reporter) { |         } else if (menuItemId == R.id.action_battery_report) { | ||||||
|             Intent intent = new Intent(); |             Intent intent = new Intent(); | ||||||
|             intent.setClass(this, BatteryReporterActivity.class); |             intent.setClass(this, BatteryReportActivity.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(); | ||||||
|   | |||||||
| @@ -0,0 +1,516 @@ | |||||||
|  | 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); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -1,51 +0,0 @@ | |||||||
| 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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										30
									
								
								powerbell/src/main/res/layout/activity_battery_report.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								powerbell/src/main/res/layout/activity_battery_report.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | <?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> | ||||||
| @@ -1,24 +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: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_reporter" |         android:id="@+id/action_battery_report" | ||||||
|         android:title="@string/item_battery_reporter"/> |         android:title="@string/item_battery_report"/> | ||||||
|     <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_reporter">Battery Reporter</string> |     <string name="item_battery_report">Battery Report</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