From c3978a1e3cdb18c6340508749d98a9d160bb4425 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 14:46:51 +0800 Subject: [PATCH] 20251022_144647_515 --- powerbell/build.gradle | 11 + powerbell/build.properties | 4 +- powerbell/src/main/AndroidManifest.xml | 12 +- .../studio/powerbell/MainActivity.java | 4 +- .../activities/BatteryReportActivity.java | 283 ++++++++++++++++++ .../activities/BatteryReporterActivity.java | 51 ---- .../res/layout/activity_battery_reporter.xml | 24 -- .../res/layout/activity_batteryreport.xml | 22 ++ 8 files changed, 331 insertions(+), 80 deletions(-) create mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java delete mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReporterActivity.java delete mode 100644 powerbell/src/main/res/layout/activity_battery_reporter.xml create mode 100644 powerbell/src/main/res/layout/activity_batteryreport.xml diff --git a/powerbell/build.gradle b/powerbell/build.gradle index 8defe52..f481063 100644 --- a/powerbell/build.gradle +++ b/powerbell/build.gradle @@ -41,6 +41,17 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + + // 允许使用系统隐藏API + lintOptions { + checkReleaseBuilds false + abortOnError false + } + // 针对PowerProfile的依赖配置 + dependenciesInfo { + includeInApk = false + includeInBundle = false + } } dependencies { diff --git a/powerbell/build.properties b/powerbell/build.properties index 4a46327..84f7a1e 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Sep 28 13:09:02 HKT 2025 +#Wed Oct 22 06:41:10 GMT 2025 stageCount=14 libraryProject= baseVersion=15.4 publishVersion=15.4.13 -buildCount=0 +buildCount=13 baseBetaVersion=15.4.14 diff --git a/powerbell/src/main/AndroidManifest.xml b/powerbell/src/main/AndroidManifest.xml index 1bfaabd..3a6684c 100644 --- a/powerbell/src/main/AndroidManifest.xml +++ b/powerbell/src/main/AndroidManifest.xml @@ -24,6 +24,14 @@ + + + + + + + + @@ -123,6 +131,8 @@ + + - \ No newline at end of file + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java index b0cce7c..a274411 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java @@ -17,7 +17,7 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.activities.AboutActivity; 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.WinBoLLActivity; import cc.winboll.studio.powerbell.beans.BackgroundPictureBean; @@ -161,7 +161,7 @@ public class MainActivity extends WinBoLLActivity { startActivity(intent); } else if (menuItemId == R.id.action_battery_reporter) { Intent intent = new Intent(); - intent.setClass(this, BatteryReporterActivity.class); + intent.setClass(this, BatteryReportActivity.class); startActivity(intent); } else if (menuItemId == R.id.action_clearrecord) { Intent intent = new Intent(); diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java new file mode 100644 index 0000000..4737eda --- /dev/null +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java @@ -0,0 +1,283 @@ +package cc.winboll.studio.powerbell.activities; + +/** + * @Author ZhanGSKen&豆包大模型 + * @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.lang.reflect.Constructor; +import java.lang.reflect.Method; +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 dataList = new ArrayList(); + private List filteredList = new ArrayList(); // 搜索过滤后的数据 + private BroadcastReceiver batteryReceiver; + private int batteryCapacity = 5400; // 手动设置电池容量(mAh,可根据设备实际容量调整) + private float lastBatteryPercent = 100.0f; // 上次电池百分比 + private long lastCheckTime = System.currentTimeMillis(); // 上次检查时间 + private EditText etSearch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_batteryreport); + + 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)); + + loadAllAppList(); // 加载所有应用(包含系统应用) + filteredList.addAll(dataList); + adapter = new BatteryReportAdapter(this, filteredList); + 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) { + filterApps(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"); + // 分配耗电到各应用 + distributeBatteryConsumption(dropPercent, getAppRunTime()); + } + + lastBatteryPercent = currentPercent; + lastCheckTime = System.currentTimeMillis(); + } + }; + registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (batteryReceiver != null) { + unregisterReceiver(batteryReceiver); + } + } + + // 加载所有应用(包含系统应用) + private void loadAllAppList() { + PackageManager pm = getPackageManager(); + List appList = pm.getInstalledApplications(PackageManager.GET_META_DATA); + + for (ApplicationInfo appInfo : appList) { + String packageName = appInfo.packageName; + String appName = pm.getApplicationLabel(appInfo).toString(); + dataList.add(new AppBatteryModel(appName, packageName, "0.0")); + } + } + + // 搜索过滤应用 + private void filterApps(String keyword) { + filteredList.clear(); + if (keyword.isEmpty()) { + filteredList.addAll(dataList); + } else { + for (AppBatteryModel model : dataList) { + if (model.getAppName().toLowerCase().contains(keyword.toLowerCase()) + || model.getPackageName().toLowerCase().contains(keyword.toLowerCase())) { + filteredList.add(model); + } + } + } + adapter.notifyDataSetChanged(); + } + + // 获取应用前台运行时长(统计24小时数据) + private Map getAppRunTime() { + Map runTimeMap = new HashMap(); + 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小时 + // 使用数值4替代INTERVAL_DAY,兼容低版本识别问题 + List statsList = manager.queryUsageStats( + 4, startTime, endTime); + for (android.app.usage.UsageStats stats : statsList) { + LogUtils.d(TAG, "应用" + stats.getPackageName() + "运行时长:" + stats.getTotalTimeInForeground()); + runTimeMap.put(stats.getPackageName(), stats.getTotalTimeInForeground()); + } + } catch (Exception e) { + LogUtils.e(TAG, "获取应用运行时长失败:" + e.getMessage()); + } + } + return runTimeMap; + } + + // 分配电池消耗到各应用,并按消耗从大到小排序 + private void distributeBatteryConsumption(float totalDropPercent, Map runTimeMap) { + long totalRunTime = 0; + for (Long time : runTimeMap.values()) { + totalRunTime += time; + } + for (AppBatteryModel model : dataList) { + Long appRunTime = runTimeMap.get(model.getPackageName()); + if (appRunTime == null) appRunTime = 0L; + float ratio = totalRunTime > 0 ? (float) appRunTime / totalRunTime : 0; + float consumption = batteryCapacity * totalDropPercent / 100 * ratio; + model.setConsumption(String.format("%.1f", consumption)); + } + + // 按电量消耗从大到小排序 + Collections.sort(dataList, new Comparator() { + @Override + public int compare(AppBatteryModel m1, AppBatteryModel m2) { + float c1 = Float.parseFloat(m1.getConsumption()); + float c2 = Float.parseFloat(m2.getConsumption()); + return Float.compare(c2, c1); // 逆序排列 + } + }); + filterApps(etSearch.getText().toString()); // 重新应用搜索过滤 + } + + 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 statsList = manager.queryUsageStats( + android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); + + return statsList != null && !statsList.isEmpty(); + } + + public static class AppBatteryModel { + private String appName; + private String packageName; + private String consumption; + + public AppBatteryModel(String appName, String packageName, String consumption) { + this.appName = appName; + this.packageName = packageName; + this.consumption = consumption; + } + + public String getAppName() { + return appName; + } + + public String getPackageName() { + return packageName; + } + + public String getConsumption() { + return consumption; + } + + public void setConsumption(String consumption) { + this.consumption = consumption; + } + } + + public static class BatteryReportAdapter extends RecyclerView.Adapter { + private Context mContext; + private List mDataList; + + public BatteryReportAdapter(Context context, List dataList) { + this.mContext = context; + this.mDataList = dataList; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + 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) { + AppBatteryModel model = mDataList.get(position); + holder.tvAppName.setText(model.getAppName()); // 显示应用名称 + holder.tvConsumption.setText("电量消耗:" + model.getConsumption() + " mAh"); + } + + @Override + public int getItemCount() { + return mDataList.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + TextView tvAppName; + TextView tvConsumption; + + public ViewHolder(View itemView) { + super(itemView); + tvAppName = (TextView) itemView.findViewById(android.R.id.text1); + tvConsumption = (TextView) itemView.findViewById(android.R.id.text2); + } + } + } +} + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReporterActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReporterActivity.java deleted file mode 100644 index f783a92..0000000 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReporterActivity.java +++ /dev/null @@ -1,51 +0,0 @@ -package cc.winboll.studio.powerbell.activities; - -/** - * @Author ZhanGSKen - * @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 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); - } -} - diff --git a/powerbell/src/main/res/layout/activity_battery_reporter.xml b/powerbell/src/main/res/layout/activity_battery_reporter.xml deleted file mode 100644 index 0ad37ee..0000000 --- a/powerbell/src/main/res/layout/activity_battery_reporter.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/powerbell/src/main/res/layout/activity_batteryreport.xml b/powerbell/src/main/res/layout/activity_batteryreport.xml new file mode 100644 index 0000000..7d1620d --- /dev/null +++ b/powerbell/src/main/res/layout/activity_batteryreport.xml @@ -0,0 +1,22 @@ + + + + + + + + +