From 44679d0c8a606fdb1038c761b21e23c1d4443688 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Sat, 27 Sep 2025 21:02:02 +0800 Subject: [PATCH 01/29] =?UTF-8?q?GlobalApplication=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- appbase/build.properties | 4 ++-- libappbase/build.properties | 4 ++-- .../cc/winboll/studio/libappbase/GlobalApplication.java | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/appbase/build.properties b/appbase/build.properties index 8e51c0ba..af3d9ab7 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Fri Sep 26 05:36:14 HKT 2025 +#Sat Sep 27 13:00:47 GMT 2025 stageCount=9 libraryProject=libappbase baseVersion=15.10 publishVersion=15.10.8 -buildCount=0 +buildCount=1 baseBetaVersion=15.10.9 diff --git a/libappbase/build.properties b/libappbase/build.properties index 8e51c0ba..af3d9ab7 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Fri Sep 26 05:36:14 HKT 2025 +#Sat Sep 27 13:00:47 GMT 2025 stageCount=9 libraryProject=libappbase baseVersion=15.10 publishVersion=15.10.8 -buildCount=0 +buildCount=1 baseBetaVersion=15.10.9 diff --git a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java index 2500de20..8c211866 100644 --- a/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java +++ b/libappbase/src/main/java/cc/winboll/studio/libappbase/GlobalApplication.java @@ -22,12 +22,12 @@ public class GlobalApplication extends Application { GlobalApplication.isDebuging = isDebuging; } - public static void saveDebugStatus(GlobalApplication application) { - APPModel.saveBeanToFile(application.getAPPModelFilePath(application), new APPModel(GlobalApplication.isDebuging)); + public static void saveDebugStatus(Context context) { + APPModel.saveBeanToFile(getAPPModelFilePath(context), new APPModel(GlobalApplication.isDebuging)); } - static String getAPPModelFilePath(GlobalApplication application) { - return application.getDataDir().getPath() + "/APPModel.json"; + static String getAPPModelFilePath(Context context) { + return context.getDataDir().getPath() + "/APPModel.json"; } public static boolean isDebuging() { From dbff19e7f4a552d6db4b5b8814c7faef0b56ca7d Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Sat, 27 Sep 2025 21:03:08 +0800 Subject: [PATCH 02/29] APK 15.10.9 release Publish. --- appbase/build.properties | 10 +++++----- libappbase/build.properties | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/appbase/build.properties b/appbase/build.properties index af3d9ab7..467f688e 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sat Sep 27 13:00:47 GMT 2025 -stageCount=9 +#Sat Sep 27 21:03:08 HKT 2025 +stageCount=10 libraryProject=libappbase baseVersion=15.10 -publishVersion=15.10.8 -buildCount=1 -baseBetaVersion=15.10.9 +publishVersion=15.10.9 +buildCount=0 +baseBetaVersion=15.10.10 diff --git a/libappbase/build.properties b/libappbase/build.properties index af3d9ab7..467f688e 100644 --- a/libappbase/build.properties +++ b/libappbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sat Sep 27 13:00:47 GMT 2025 -stageCount=9 +#Sat Sep 27 21:03:08 HKT 2025 +stageCount=10 libraryProject=libappbase baseVersion=15.10 -publishVersion=15.10.8 -buildCount=1 -baseBetaVersion=15.10.9 +publishVersion=15.10.9 +buildCount=0 +baseBetaVersion=15.10.10 From e9bb789daa3a8faf30e0d29028bbf30e4e436ec3 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Sat, 27 Sep 2025 21:03:24 +0800 Subject: [PATCH 03/29] Library Release 15.10.9 --- appbase/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appbase/build.properties b/appbase/build.properties index 467f688e..3b345de0 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,5 +1,5 @@ #Created by .winboll/winboll_app_build.gradle -#Sat Sep 27 21:03:08 HKT 2025 +#Sat Sep 27 21:03:20 HKT 2025 stageCount=10 libraryProject=libappbase baseVersion=15.10 From 963a3bb7cd95540ffd82989d771c2831a6a4ab77 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Sun, 28 Sep 2025 13:07:02 +0800 Subject: [PATCH 04/29] =?UTF-8?q?=E7=BC=96=E8=AF=91=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 643904b7..74893242 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Sep 03 20:59:53 HKT 2025 +#Sun Sep 28 05:06:35 GMT 2025 stageCount=13 libraryProject= baseVersion=15.4 publishVersion=15.4.12 -buildCount=0 +buildCount=2 baseBetaVersion=15.4.13 From 5e198d9c6868cfa6269bae25b05e103dc5f22cbe Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Sun, 28 Sep 2025 13:09:02 +0800 Subject: [PATCH 05/29] APK 15.4.13 release Publish. --- powerbell/build.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 74893242..4a463274 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Sep 28 05:06:35 GMT 2025 -stageCount=13 +#Sun Sep 28 13:09:02 HKT 2025 +stageCount=14 libraryProject= baseVersion=15.4 -publishVersion=15.4.12 -buildCount=2 -baseBetaVersion=15.4.13 +publishVersion=15.4.13 +buildCount=0 +baseBetaVersion=15.4.14 From c3978a1e3cdb18c6340508749d98a9d160bb4425 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 14:46:51 +0800 Subject: [PATCH 06/29] 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 8defe521..f4810637 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 4a463274..84f7a1ea 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 1bfaabdf..3a6684cc 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 b0cce7ce..a2744118 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 00000000..4737eda9 --- /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 f783a924..00000000 --- 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 0ad37ee1..00000000 --- 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 00000000..7d1620df --- /dev/null +++ b/powerbell/src/main/res/layout/activity_batteryreport.xml @@ -0,0 +1,22 @@ + + + + + + + + + From f7b2c0d4c0110f872b63795b879052022932cae0 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 16:56:17 +0800 Subject: [PATCH 07/29] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E7=94=B5=E9=87=8F=E4=BD=BF=E7=94=A8=E6=83=85=E5=86=B5=E6=8A=A5?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- powerbell/src/main/AndroidManifest.xml | 12 + .../activities/BatteryReportActivity.java | 371 ++++++++++++++---- .../res/layout/activity_batteryreport.xml | 23 +- 4 files changed, 334 insertions(+), 76 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 84f7a1ea..5c55b08a 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 06:41:10 GMT 2025 +#Wed Oct 22 08:55:04 GMT 2025 stageCount=14 libraryProject= baseVersion=15.4 publishVersion=15.4.13 -buildCount=13 +buildCount=43 baseBetaVersion=15.4.14 diff --git a/powerbell/src/main/AndroidManifest.xml b/powerbell/src/main/AndroidManifest.xml index 3a6684cc..e5940409 100644 --- a/powerbell/src/main/AndroidManifest.xml +++ b/powerbell/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ @@ -35,7 +36,18 @@ + + + + + + + + + dataList = new ArrayList(); - private List filteredList = new ArrayList(); // 搜索过滤后的数据 + // 数据列表:仅存储包名、耗电、时长(不存储名称,计算全程用包名) + 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 int batteryCapacity = 5400; // 电池容量(mAh) + private float lastBatteryPercent = 100.0f; + private long lastCheckTime = System.currentTimeMillis(); private EditText etSearch; + // 应用运行时长缓存(key:包名,value:时长ms) + private Map appRunTimeCache = new HashMap(); + // 包管理工具(全局持有,避免重复获取) + private PackageManager mPackageManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_batteryreport); + // 初始化包管理工具(仅一次,供后续查询名称使用) + 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); + etSearch = (EditText) findViewById(R.id.et_search); + rvBatteryReport = (RecyclerView) findViewById(R.id.rv_battery_report); rvBatteryReport.setLayoutManager(new LinearLayoutManager(this)); - loadAllAppList(); // 加载所有应用(包含系统应用) + // 1. 加载所有应用(仅存包名,不查名称) + loadAllAppPackage(); + // 2. 强制添加目标应用(包名兜底) + //forceAddTargetPackage(); + // 3. 首次获取运行时长(key:包名) + appRunTimeCache = getAppRunTime(); + // 4. 更新时长到数据模型(用包名匹配) + updateAppRunTimeToModel(); + // 5. 初始化过滤列表和适配器(传入包管理工具供显示时查名称) filteredList.addAll(dataList); - adapter = new BatteryReportAdapter(this, filteredList); + adapter = new BatteryReportAdapter(this, filteredList, mPackageManager); 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()); + filterAppsByPackage(s.toString()); } @Override public void afterTextChanged(Editable s) {} }); - // 注册电池广播监听 + // 电池广播(耗电计算全程用包名) batteryReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -92,13 +108,17 @@ public class BatteryReportActivity extends Activity { 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()); + // 重新获取最新运行时长(key:包名) + appRunTimeCache = getAppRunTime(); + // 更新模型中的运行时长(包名匹配) + updateAppRunTimeToModel(); + // 分配耗电(包名计算) + distributeBatteryConsumption(dropPercent, appRunTimeCache); } lastBatteryPercent = currentPercent; @@ -111,95 +131,248 @@ public class BatteryReportActivity extends Activity { @Override protected void onDestroy() { super.onDestroy(); + // Java7 显式非空判断 if (batteryReceiver != null) { unregisterReceiver(batteryReceiver); } } - // 加载所有应用(包含系统应用) - private void loadAllAppList() { - PackageManager pm = getPackageManager(); - List appList = pm.getInstalledApplications(PackageManager.GET_META_DATA); + /** + * 加载所有应用(仅获取包名,不查询应用名称,数据计算核心步骤) + */ + private void loadAllAppPackage() { + List appList = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA); + dataList.clear(); // 清空列表避免重复 + LogUtils.d(TAG, "开始加载应用包名列表,共找到" + appList.size() + "个应用"); + + // 仅遍历包名,不处理名称(避免名称获取异常影响) for (ApplicationInfo appInfo : appList) { String packageName = appInfo.packageName; - String appName = pm.getApplicationLabel(appInfo).toString(); - dataList.add(new AppBatteryModel(appName, packageName, "0.0")); + /*if (packageName.equals("cc.winboll.studio.powerbell.beta")) { + //if (packageName.equals("aidepro.top")) { + LogUtils.d(TAG, "aidepro.top OK5"); + }*/ + // 模型仅存包名、初始耗电0、初始时长0(名称显示时再查) + dataList.add(new AppBatteryModel(packageName, "0.0", 0)); } + + /*for (AppBatteryModel appBatteryModel : dataList) { + if (appBatteryModel.getPackageName().equals("aidepro.top")) { + LogUtils.d(TAG, "aidepro.top OK4"); + } + }*/ + + LogUtils.d(TAG, "应用包名列表加载完成,共添加" + dataList.size() + "个包名。" ); } - // 搜索过滤应用 - private void filterApps(String keyword) { + /** + * 强制添加目标包名(兜底逻辑,仅操作包名) + */ + private void forceAddTargetPackage() { + // 1. 先检查列表中是否已存在目标包名 + boolean isTargetExists = false; + for (AppBatteryModel model : dataList) { + if (TARGET_APP_PACKAGE.equals(model.getPackageName())) { + isTargetExists = true; + break; + } + } + + if (isTargetExists) { + LogUtils.d(TAG, "目标包名已在列表中,无需重复添加"); + return; + } + + // 2. 仅用包名创建模型,强制添加(不依赖名称查询) + dataList.add(0, new AppBatteryModel(TARGET_APP_PACKAGE, "0.0", 0)); + LogUtils.d(TAG, "强制添加目标包名成功:" + TARGET_APP_PACKAGE); + } + + /** + * 更新运行时长到模型(用包名匹配,不涉及名称) + */ + private void updateAppRunTimeToModel() { + /*if(appRunTimeCache.containsKey("aidepro.top")) + { + LogUtils.d(TAG, "aidepro.top OK2"); + }*/ + + int nCount = 0; + for (AppBatteryModel model : dataList) { + String packageName = model.getPackageName(); + /*if(packageName.equals("aidepro.top")) + { + LogUtils.d(TAG, "aidepro.top OK3"); + }*/ + + Long runTime; + // Java7 显式判断包名是否在缓存中 + if (appRunTimeCache.containsKey(packageName)) { + /*if(packageName.equals("cc.winboll.studio.powerbell.beta")) { + LogUtils.d(TAG, String.format("特殊查询 %s 查询有结果。", 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)); + } + + /** + * 按包名过滤应用(搜索逻辑仅操作包名) + */ + private void filterAppsByPackage(String keyword) { filteredList.clear(); - if (keyword.isEmpty()) { + if (keyword == null || keyword.isEmpty()) { filteredList.addAll(dataList); } else { + String lowerKeyword = keyword.toLowerCase(); + boolean isTargetMatched = false; + for (AppBatteryModel model : dataList) { - if (model.getAppName().toLowerCase().contains(keyword.toLowerCase()) - || model.getPackageName().toLowerCase().contains(keyword.toLowerCase())) { + String packageNameLower = model.getPackageName().toLowerCase(); + // 仅按包名包含关键词过滤 + boolean isMatched = packageNameLower.contains(lowerKeyword); + + if (isMatched) { filteredList.add(model); + // 检查是否是目标包名 + if (TARGET_APP_PACKAGE.equals(model.getPackageName())) { + isTargetMatched = true; + } + } + } + + // 兜底:目标包名未匹配时强制添加 + if (!isTargetMatched) { + for (AppBatteryModel model : dataList) { + if (TARGET_APP_PACKAGE.equals(model.getPackageName())) { + filteredList.add(0, model); + LogUtils.d(TAG, "搜索兜底:强制添加目标包名到结果列表"); + break; + } } } } 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); + 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); + android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); + + // 仅存储包名和时长(不查名称) for (android.app.usage.UsageStats stats : statsList) { - LogUtils.d(TAG, "应用" + stats.getPackageName() + "运行时长:" + stats.getTotalTimeInForeground()); - runTimeMap.put(stats.getPackageName(), stats.getTotalTimeInForeground()); + long runTimeMs = stats.getTotalTimeInForeground(); + String packageName = stats.getPackageName(); + LogUtils.d(TAG, "包名" + packageName + "运行时长:" + 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()); } } + + /*if(runTimeMap.containsKey("aidepro.top")) + { + LogUtils.d(TAG, "aidepro.top OK"); + }*/ + + 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 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)); + // 遍历包名计算总时长 + for (Map.Entry entry : runTimeMap.entrySet()) { + totalRunTime += entry.getValue(); } - // 按电量消耗从大到小排序 + // 按包名匹配计算各应用耗电 + for (AppBatteryModel model : dataList) { + String packageName = model.getPackageName(); + Long appRunTime; + // Java7 显式判断包名是否存在 + if (runTimeMap.containsKey(packageName)) { + appRunTime = runTimeMap.get(packageName); + } else { + appRunTime = 0L; + } + + float ratio = (totalRunTime > 0) ? (float) appRunTime / totalRunTime : 0; + float consumption = batteryCapacity * totalDropPercent / 100 * ratio; + model.setConsumption(String.format("%.1f", consumption)); + model.setRunTime(appRunTime); // 同步更新时长 + } + + // 按耗电排序(用包名对应的耗电值排序) 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); // 逆序排列 + return Float.compare(c2, c1); // 逆序(耗电从高到低) } }); - filterApps(etSearch.getText().toString()); // 重新应用搜索过滤 + + // 重新应用包名过滤 + filterAppsByPackage(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); + android.app.usage.UsageStatsManager manager = + (android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); if (manager == null) { return false; } @@ -207,26 +380,27 @@ public class BatteryReportActivity extends Activity { long endTime = System.currentTimeMillis(); long startTime = endTime - 1000 * 60; List statsList = manager.queryUsageStats( - android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); + 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; + private String packageName; // 核心标识:应用包名 + private String consumption; // 电量消耗(mAh) + private long runTime; // 运行时长(ms) - public AppBatteryModel(String appName, String packageName, String consumption) { - this.appName = appName; + // Java7 显式构造(仅传入包名、初始耗电、初始时长) + public AppBatteryModel(String packageName, String consumption, long runTime) { this.packageName = packageName; this.consumption = consumption; + this.runTime = runTime; } - public String getAppName() { - return appName; - } - + // Getter/Setter(仅操作包名、耗电、时长) public String getPackageName() { return packageName; } @@ -238,42 +412,105 @@ public class BatteryReportActivity extends Activity { public void setConsumption(String consumption) { this.consumption = consumption; } + + public long getRunTime() { + return runTime; + } + + public void setRunTime(long runTime) { + this.runTime = runTime; + } } + /** + * RecyclerView 适配器(仅在显示时通过包名查询应用名称) + */ public static class BatteryReportAdapter extends RecyclerView.Adapter { private Context mContext; private List mDataList; + private PackageManager mPm; // 用于显示时查询应用名称 - public BatteryReportAdapter(Context context, List dataList) { + // Java7 显式构造(传入包管理工具) + public BatteryReportAdapter(Context context, List dataList, PackageManager pm) { this.mContext = context; this.mDataList = dataList; + this.mPm = pm; // 持有包管理工具,供显示时查名称 } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(mContext) - .inflate(android.R.layout.simple_list_item_2, parent, false); + .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); - holder.tvAppName.setText(model.getAppName()); // 显示应用名称 - holder.tvConsumption.setText("电量消耗:" + model.getConsumption() + " mAh"); + String packageName = model.getPackageName(); + String appName = ""; + + // 关键步骤:通过包名查询应用名称(仅显示时执行,不影响数据计算) + try { + ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0); + appName = mPm.getApplicationLabel(appInfo).toString(); + } catch (PackageManager.NameNotFoundException e) { + // 包名不存在(如应用已卸载),用包名作为名称兜底 + appName = packageName; + LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage()); + } catch (Exception e) { + // 其他异常(如权限问题),同样用包名兜底 + appName = packageName; + LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage()); + } + + // 显示应用名称(查询到则显示名称,否则显示包名) + holder.tvAppName.setText(appName); + + // 格式化运行时长(调用 Activity 的工具方法) + String runTimeStr = ((BatteryReportActivity) mContext).formatRunTime(model.getRunTime()); + // 显示耗电+运行时长(基于包名计算的结果) + String consumptionText = String.format("耗电:%s mAh | 运行时长:%s", + model.getConsumption(), runTimeStr); + holder.tvConsumption.setText(consumptionText); + + // 优化显示:目标应用文字标蓝(通过包名匹配) + if (BatteryReportActivity.TARGET_APP_PACKAGE.equals(packageName)) { + holder.tvAppName.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); + holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); + } else { + 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.size(); + return mDataList == null ? 0 : mDataList.size(); } + // ViewHolder(绑定系统布局的两个 TextView) public static class ViewHolder extends RecyclerView.ViewHolder { - TextView tvAppName; - TextView tvConsumption; + TextView tvAppName; // 显示应用名称(通过包名查询) + TextView tvConsumption; // 显示耗电+运行时长(包名计算结果) + // Java7 显式构造 public ViewHolder(View itemView) { super(itemView); + // 强制类型转换(系统布局控件 ID:text1 和 text2) tvAppName = (TextView) itemView.findViewById(android.R.id.text1); tvConsumption = (TextView) itemView.findViewById(android.R.id.text2); } diff --git a/powerbell/src/main/res/layout/activity_batteryreport.xml b/powerbell/src/main/res/layout/activity_batteryreport.xml index 7d1620df..70a6d3e8 100644 --- a/powerbell/src/main/res/layout/activity_batteryreport.xml +++ b/powerbell/src/main/res/layout/activity_batteryreport.xml @@ -1,22 +1,31 @@ - + android:orientation="vertical" + android:background="@android:color/white"> + + android:layout_margin="8dp" + android:padding="12dp" + android:hint="搜索应用包名" + android:background="@android:drawable/btn_default_small" + android:inputType="text" + android:textSize="16sp"/> + + android:layout_height="0dp" + android:layout_weight="1" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp"/> From 561330697b03a2a4b5b896fccdb9e48b680c7c74 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 16:57:22 +0800 Subject: [PATCH 08/29] APK 15.4.14 release Publish. --- powerbell/build.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 5c55b08a..1dfe6504 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 08:55:04 GMT 2025 -stageCount=14 +#Wed Oct 22 16:57:21 HKT 2025 +stageCount=15 libraryProject= baseVersion=15.4 -publishVersion=15.4.13 -buildCount=43 -baseBetaVersion=15.4.14 +publishVersion=15.4.14 +buildCount=0 +baseBetaVersion=15.4.15 From e5c8624d9b5e8d9cc0c6c5193a3169c76a594f3f Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 17:44:12 +0800 Subject: [PATCH 09/29] =?UTF-8?q?=E5=BA=94=E7=94=A8=E7=94=B5=E9=87=8F?= =?UTF-8?q?=E6=8A=A5=E5=91=8A=E4=B8=AD=E6=B7=BB=E5=8A=A0=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E6=90=9C=E7=B4=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../activities/BatteryReportActivity.java | 191 ++++++++++-------- .../res/layout/activity_batteryreport.xml | 7 +- 3 files changed, 115 insertions(+), 87 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 1dfe6504..99402ffc 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 16:57:21 HKT 2025 +#Wed Oct 22 09:39:33 GMT 2025 stageCount=15 libraryProject= baseVersion=15.4 publishVersion=15.4.14 -buildCount=0 +buildCount=1 baseBetaVersion=15.4.15 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 index 42975b3d..b824260a 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java @@ -51,6 +51,8 @@ public class BatteryReportActivity extends Activity { private EditText etSearch; // 应用运行时长缓存(key:包名,value:时长ms) private Map appRunTimeCache = new HashMap(); + // 新增:包名-应用名称映射缓存(用于搜索匹配名称,避免重复查询) + private Map packageToAppNameCache = new HashMap(); // 包管理工具(全局持有,避免重复获取) private PackageManager mPackageManager; @@ -74,25 +76,27 @@ public class BatteryReportActivity extends Activity { // 1. 加载所有应用(仅存包名,不查名称) loadAllAppPackage(); - // 2. 强制添加目标应用(包名兜底) + // 2. 预缓存所有包名对应的应用名称(为搜索名称做准备) + preCacheAllAppNames(); + // 3. 强制添加目标应用(包名兜底) //forceAddTargetPackage(); - // 3. 首次获取运行时长(key:包名) + // 4. 首次获取运行时长(key:包名) appRunTimeCache = getAppRunTime(); - // 4. 更新时长到数据模型(用包名匹配) + // 5. 更新时长到数据模型(用包名匹配) updateAppRunTimeToModel(); - // 5. 初始化过滤列表和适配器(传入包管理工具供显示时查名称) + // 6. 初始化过滤列表和适配器(传入包管理工具+名称缓存) filteredList.addAll(dataList); - adapter = new BatteryReportAdapter(this, filteredList, mPackageManager); + 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) { - filterAppsByPackage(s.toString()); + filterAppsByPackageAndName(s.toString()); // 替换为新的双维度过滤方法 } @Override @@ -145,27 +149,51 @@ public class BatteryReportActivity extends Activity { dataList.clear(); // 清空列表避免重复 LogUtils.d(TAG, "开始加载应用包名列表,共找到" + appList.size() + "个应用"); - + // 仅遍历包名,不处理名称(避免名称获取异常影响) for (ApplicationInfo appInfo : appList) { String packageName = appInfo.packageName; - /*if (packageName.equals("cc.winboll.studio.powerbell.beta")) { - //if (packageName.equals("aidepro.top")) { - LogUtils.d(TAG, "aidepro.top OK5"); - }*/ - // 模型仅存包名、初始耗电0、初始时长0(名称显示时再查) + // 模型仅存包名、初始耗电0、初始时长0(名称显示/搜索时用缓存) dataList.add(new AppBatteryModel(packageName, "0.0", 0)); } - - /*for (AppBatteryModel appBatteryModel : dataList) { - if (appBatteryModel.getPackageName().equals("aidepro.top")) { - LogUtils.d(TAG, "aidepro.top OK4"); - } - }*/ 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; + } + } + /** * 强制添加目标包名(兜底逻辑,仅操作包名) */ @@ -186,6 +214,8 @@ public class BatteryReportActivity extends Activity { // 2. 仅用包名创建模型,强制添加(不依赖名称查询) dataList.add(0, new AppBatteryModel(TARGET_APP_PACKAGE, "0.0", 0)); + // 同步更新名称缓存 + packageToAppNameCache.put(TARGET_APP_PACKAGE, getAppNameByPackage(TARGET_APP_PACKAGE)); LogUtils.d(TAG, "强制添加目标包名成功:" + TARGET_APP_PACKAGE); } @@ -193,57 +223,51 @@ public class BatteryReportActivity extends Activity { * 更新运行时长到模型(用包名匹配,不涉及名称) */ private void updateAppRunTimeToModel() { - /*if(appRunTimeCache.containsKey("aidepro.top")) - { - LogUtils.d(TAG, "aidepro.top OK2"); - }*/ - - int nCount = 0; + int nCount = 0; for (AppBatteryModel model : dataList) { String packageName = model.getPackageName(); - /*if(packageName.equals("aidepro.top")) - { - LogUtils.d(TAG, "aidepro.top OK3"); - }*/ - Long runTime; // Java7 显式判断包名是否在缓存中 if (appRunTimeCache.containsKey(packageName)) { - /*if(packageName.equals("cc.winboll.studio.powerbell.beta")) { - LogUtils.d(TAG, String.format("特殊查询 %s 查询有结果。", packageName)); - }*/ runTime = appRunTimeCache.get(packageName); - LogUtils.d(TAG, String.format("应用包 %s 运行时长已更新。", packageName)); - nCount++; + 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)); + LogUtils.d(TAG, String.format("dataList.size() %d, appRunTimeCache.size() %d。", dataList.size(), appRunTimeCache.size())); + LogUtils.d(TAG, String.format("updateAppRunTimeToModel() 更新的数据量为:%d", nCount)); } /** - * 按包名过滤应用(搜索逻辑仅操作包名) + * 新增:双维度过滤(包名+应用名称) + * 搜索关键词同时匹配“包名(不区分大小写)”或“应用名称(不区分大小写)” */ - private void filterAppsByPackage(String keyword) { + private void filterAppsByPackageAndName(String keyword) { filteredList.clear(); if (keyword == null || keyword.isEmpty()) { filteredList.addAll(dataList); } else { - String lowerKeyword = keyword.toLowerCase(); + String lowerKeyword = keyword.toLowerCase(); // 统一转为小写,实现不区分大小写搜索 boolean isTargetMatched = false; for (AppBatteryModel model : dataList) { - String packageNameLower = model.getPackageName().toLowerCase(); - // 仅按包名包含关键词过滤 - boolean isMatched = packageNameLower.contains(lowerKeyword); + String packageName = model.getPackageName(); + String packageNameLower = packageName.toLowerCase(); + // 从缓存中获取应用名称(避免重复查询PackageManager) + String appName = packageToAppNameCache.get(packageName); + String appNameLower = appName.toLowerCase(); + + // 匹配规则:包名包含关键词 OR 应用名称包含关键词 + boolean isMatched = packageNameLower.contains(lowerKeyword) + || appNameLower.contains(lowerKeyword); if (isMatched) { filteredList.add(model); // 检查是否是目标包名 - if (TARGET_APP_PACKAGE.equals(model.getPackageName())) { + if (TARGET_APP_PACKAGE.equals(packageName)) { isTargetMatched = true; } } @@ -271,11 +295,11 @@ public class BatteryReportActivity extends Activity { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { try { android.app.usage.UsageStatsManager manager = - (android.app.usage.UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); + (android.app.usage.UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); long endTime = System.currentTimeMillis(); long startTime = endTime - 24 * 3600 * 1000; // 近24小时 List statsList = manager.queryUsageStats( - android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); + android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); // 仅存储包名和时长(不查名称) for (android.app.usage.UsageStats stats : statsList) { @@ -283,21 +307,16 @@ public class BatteryReportActivity extends Activity { String packageName = stats.getPackageName(); LogUtils.d(TAG, "包名" + packageName + "运行时长:" + formatRunTime(runTimeMs)); runTimeMap.put(packageName, runTimeMs); - if (packageName.equals("aidepro.top")) { - LogUtils.d(TAG, String.format("runTimeMap.put(packageName, runTimeMs) 特殊查询 %s 查询有结果。", packageName)); - } + if (packageName.equals("aidepro.top")) { + LogUtils.d(TAG, String.format("runTimeMap.put(packageName, runTimeMs) 特殊查询 %s 查询有结果。", packageName)); + } } } catch (Exception e) { LogUtils.e(TAG, "获取应用运行时长失败:" + e.getMessage()); } } - /*if(runTimeMap.containsKey("aidepro.top")) - { - LogUtils.d(TAG, "aidepro.top OK"); - }*/ - - LogUtils.d(TAG, String.format("应用运行时长列表数量%d。", runTimeMap.size())); + LogUtils.d(TAG, String.format("应用运行时长列表数量%d。", runTimeMap.size())); return runTimeMap; } @@ -359,8 +378,8 @@ public class BatteryReportActivity extends Activity { } }); - // 重新应用包名过滤 - filterAppsByPackage(etSearch.getText().toString()); + // 重新应用“包名+名称”双维度过滤 + filterAppsByPackageAndName(etSearch.getText().toString()); } /** @@ -372,7 +391,7 @@ public class BatteryReportActivity extends Activity { } android.app.usage.UsageStatsManager manager = - (android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); + (android.app.usage.UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); if (manager == null) { return false; } @@ -380,7 +399,7 @@ public class BatteryReportActivity extends Activity { long endTime = System.currentTimeMillis(); long startTime = endTime - 1000 * 60; List statsList = manager.queryUsageStats( - android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); + android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime); return statsList != null && !statsList.isEmpty(); } @@ -423,28 +442,31 @@ public class BatteryReportActivity extends Activity { } /** - * RecyclerView 适配器(仅在显示时通过包名查询应用名称) + * RecyclerView 适配器(支持从缓存获取名称,避免重复查询) */ public static class BatteryReportAdapter extends RecyclerView.Adapter { private Context mContext; private List mDataList; - private PackageManager mPm; // 用于显示时查询应用名称 + private PackageManager mPm; // 兜底用:缓存无名称时查询 + private Map mPackageToNameCache; // 新增:名称缓存映射 - // Java7 显式构造(传入包管理工具) - public BatteryReportAdapter(Context context, List dataList, PackageManager pm) { + // Java7 显式构造(新增名称缓存参数) + public BatteryReportAdapter(Context context, List dataList, + PackageManager pm, Map packageToNameCache) { this.mContext = context; this.mDataList = dataList; - this.mPm = pm; // 持有包管理工具,供显示时查名称 + this.mPm = pm; + this.mPackageToNameCache = packageToNameCache; // 接收名称缓存 } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(mContext) - .inflate(android.R.layout.simple_list_item_2, parent, false); + .inflate(android.R.layout.simple_list_item_2, parent, false); return new ViewHolder(itemView); } - // 绑定数据:核心修改——仅在此处通过包名查询应用名称并显示 + // 绑定数据:优先从缓存取名称,无缓存时兜底查询 @Override public void onBindViewHolder(ViewHolder holder, int position) { // Java7 显式非空判断(避免空指针) @@ -458,28 +480,35 @@ public class BatteryReportActivity extends Activity { String packageName = model.getPackageName(); String appName = ""; - // 关键步骤:通过包名查询应用名称(仅显示时执行,不影响数据计算) - try { - ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0); - appName = mPm.getApplicationLabel(appInfo).toString(); - } catch (PackageManager.NameNotFoundException e) { - // 包名不存在(如应用已卸载),用包名作为名称兜底 - appName = packageName; - LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage()); - } catch (Exception e) { - // 其他异常(如权限问题),同样用包名兜底 - appName = packageName; - LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage()); + // 优先从缓存获取名称(避免重复调用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()); + } } - // 显示应用名称(查询到则显示名称,否则显示包名) + // 显示应用名称(缓存/兜底结果) holder.tvAppName.setText(appName); // 格式化运行时长(调用 Activity 的工具方法) String runTimeStr = ((BatteryReportActivity) mContext).formatRunTime(model.getRunTime()); // 显示耗电+运行时长(基于包名计算的结果) String consumptionText = String.format("耗电:%s mAh | 运行时长:%s", - model.getConsumption(), runTimeStr); + model.getConsumption(), runTimeStr); holder.tvConsumption.setText(consumptionText); // 优化显示:目标应用文字标蓝(通过包名匹配) @@ -504,7 +533,7 @@ public class BatteryReportActivity extends Activity { // ViewHolder(绑定系统布局的两个 TextView) public static class ViewHolder extends RecyclerView.ViewHolder { - TextView tvAppName; // 显示应用名称(通过包名查询) + TextView tvAppName; // 显示应用名称(缓存/兜底获取) TextView tvConsumption; // 显示耗电+运行时长(包名计算结果) // Java7 显式构造 diff --git a/powerbell/src/main/res/layout/activity_batteryreport.xml b/powerbell/src/main/res/layout/activity_batteryreport.xml index 70a6d3e8..361b96fe 100644 --- a/powerbell/src/main/res/layout/activity_batteryreport.xml +++ b/powerbell/src/main/res/layout/activity_batteryreport.xml @@ -6,19 +6,19 @@ android:orientation="vertical" android:background="@android:color/white"> - + - + - From 4108371c2008c16761442cbd366195906b0714b5 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 17:45:14 +0800 Subject: [PATCH 10/29] APK 15.4.15 release Publish. --- powerbell/build.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 99402ffc..496d4d83 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 09:39:33 GMT 2025 -stageCount=15 +#Wed Oct 22 17:45:14 HKT 2025 +stageCount=16 libraryProject= baseVersion=15.4 -publishVersion=15.4.14 -buildCount=1 -baseBetaVersion=15.4.15 +publishVersion=15.4.15 +buildCount=0 +baseBetaVersion=15.4.16 From ca4e4c7febf6abbbc0d879868e3509fb76699807 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 18:26:54 +0800 Subject: [PATCH 11/29] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 ++-- .../main/java/cc/winboll/studio/powerbell/MainActivity.java | 2 +- .../studio/powerbell/activities/BatteryReportActivity.java | 2 +- ...activity_batteryreport.xml => activity_battery_report.xml} | 0 powerbell/src/main/res/menu/toolbar_main.xml | 4 ++-- powerbell/src/main/res/values/strings.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename powerbell/src/main/res/layout/{activity_batteryreport.xml => activity_battery_report.xml} (100%) diff --git a/powerbell/build.properties b/powerbell/build.properties index 496d4d83..c41fd01a 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 17:45:14 HKT 2025 +#Wed Oct 22 10:25:50 GMT 2025 stageCount=16 libraryProject= baseVersion=15.4 publishVersion=15.4.15 -buildCount=0 +buildCount=1 baseBetaVersion=15.4.16 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 a2744118..6bd6a546 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java @@ -159,7 +159,7 @@ public class MainActivity extends WinBoLLActivity { if (menuItemId == R.id.action_about) { Intent intent = new Intent(this, AboutActivity.class); startActivity(intent); - } else if (menuItemId == R.id.action_battery_reporter) { + } else if (menuItemId == R.id.action_battery_report) { Intent intent = new Intent(); intent.setClass(this, BatteryReportActivity.class); startActivity(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 index b824260a..9e63ea56 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java @@ -59,7 +59,7 @@ public class BatteryReportActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_batteryreport); + setContentView(R.layout.activity_battery_report); // 初始化包管理工具(仅一次,供后续查询名称使用) mPackageManager = getPackageManager(); diff --git a/powerbell/src/main/res/layout/activity_batteryreport.xml b/powerbell/src/main/res/layout/activity_battery_report.xml similarity index 100% rename from powerbell/src/main/res/layout/activity_batteryreport.xml rename to powerbell/src/main/res/layout/activity_battery_report.xml diff --git a/powerbell/src/main/res/menu/toolbar_main.xml b/powerbell/src/main/res/menu/toolbar_main.xml index 77ba849b..5ef6f1b5 100644 --- a/powerbell/src/main/res/menu/toolbar_main.xml +++ b/powerbell/src/main/res/menu/toolbar_main.xml @@ -1,8 +1,8 @@ + android:id="@+id/action_battery_report" + android:title="@string/item_battery_report"/> diff --git a/powerbell/src/main/res/values/strings.xml b/powerbell/src/main/res/values/strings.xml index 6206849b..1b7491c8 100644 --- a/powerbell/src/main/res/values/strings.xml +++ b/powerbell/src/main/res/values/strings.xml @@ -6,7 +6,7 @@ This application has crashed, the author level is limited, please understand! Main View About - Battery Reporter + Battery Report Clear Record Change Picture Developer View From 10b8da2e2140ba91708b5441ceb989b8c9d13aee Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 18:35:29 +0800 Subject: [PATCH 12/29] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E7=94=B5=E9=87=8F=E6=8A=A5=E5=91=8A=E6=98=BE=E7=A4=BA=E9=A3=8E?= =?UTF-8?q?=E6=A0=BC=EF=BC=8C=E7=B2=BE=E7=AE=80=E5=86=97=E4=BD=99=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../activities/BatteryReportActivity.java | 60 +++---------------- 2 files changed, 10 insertions(+), 54 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index c41fd01a..52f43357 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 10:25:50 GMT 2025 +#Wed Oct 22 10:33:32 GMT 2025 stageCount=16 libraryProject= baseVersion=15.4 publishVersion=15.4.15 -buildCount=1 +buildCount=2 baseBetaVersion=15.4.16 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 index 9e63ea56..7a6bf84b 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java @@ -36,8 +36,6 @@ import cc.winboll.studio.libappbase.LogUtils; public class BatteryReportActivity extends Activity { public static final String TAG = "BatteryReportActivity"; - // 目标应用包名常量(统一维护,数据计算核心标识) - private static final String TARGET_APP_PACKAGE = "cc.winboll.studio.mymessagemanager"; private RecyclerView rvBatteryReport; private BatteryReportAdapter adapter; @@ -78,8 +76,6 @@ public class BatteryReportActivity extends Activity { loadAllAppPackage(); // 2. 预缓存所有包名对应的应用名称(为搜索名称做准备) preCacheAllAppNames(); - // 3. 强制添加目标应用(包名兜底) - //forceAddTargetPackage(); // 4. 首次获取运行时长(key:包名) appRunTimeCache = getAppRunTime(); // 5. 更新时长到数据模型(用包名匹配) @@ -157,7 +153,7 @@ public class BatteryReportActivity extends Activity { dataList.add(new AppBatteryModel(packageName, "0.0", 0)); } - LogUtils.d(TAG, "应用包名列表加载完成,共添加" + dataList.size() + "个包名。" ); + LogUtils.d(TAG, "应用包名列表加载完成,共添加" + dataList.size() + "个包名。"); } /** @@ -194,31 +190,6 @@ public class BatteryReportActivity extends Activity { } } - /** - * 强制添加目标包名(兜底逻辑,仅操作包名) - */ - private void forceAddTargetPackage() { - // 1. 先检查列表中是否已存在目标包名 - boolean isTargetExists = false; - for (AppBatteryModel model : dataList) { - if (TARGET_APP_PACKAGE.equals(model.getPackageName())) { - isTargetExists = true; - break; - } - } - - if (isTargetExists) { - LogUtils.d(TAG, "目标包名已在列表中,无需重复添加"); - return; - } - - // 2. 仅用包名创建模型,强制添加(不依赖名称查询) - dataList.add(0, new AppBatteryModel(TARGET_APP_PACKAGE, "0.0", 0)); - // 同步更新名称缓存 - packageToAppNameCache.put(TARGET_APP_PACKAGE, getAppNameByPackage(TARGET_APP_PACKAGE)); - LogUtils.d(TAG, "强制添加目标包名成功:" + TARGET_APP_PACKAGE); - } - /** * 更新运行时长到模型(用包名匹配,不涉及名称) */ @@ -251,7 +222,6 @@ public class BatteryReportActivity extends Activity { filteredList.addAll(dataList); } else { String lowerKeyword = keyword.toLowerCase(); // 统一转为小写,实现不区分大小写搜索 - boolean isTargetMatched = false; for (AppBatteryModel model : dataList) { String packageName = model.getPackageName(); @@ -266,21 +236,7 @@ public class BatteryReportActivity extends Activity { if (isMatched) { filteredList.add(model); - // 检查是否是目标包名 - if (TARGET_APP_PACKAGE.equals(packageName)) { - isTargetMatched = true; - } - } - } - // 兜底:目标包名未匹配时强制添加 - if (!isTargetMatched) { - for (AppBatteryModel model : dataList) { - if (TARGET_APP_PACKAGE.equals(model.getPackageName())) { - filteredList.add(0, model); - LogUtils.d(TAG, "搜索兜底:强制添加目标包名到结果列表"); - break; - } } } } @@ -512,13 +468,13 @@ public class BatteryReportActivity extends Activity { holder.tvConsumption.setText(consumptionText); // 优化显示:目标应用文字标蓝(通过包名匹配) - if (BatteryReportActivity.TARGET_APP_PACKAGE.equals(packageName)) { - holder.tvAppName.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); - holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); - } else { - holder.tvAppName.setTextColor(mContext.getResources().getColor(android.R.color.black)); - holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.darker_gray)); - } + + holder.tvAppName.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); + holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); + + //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); From de34c33706854377e5286448ba9593fbc0b58768 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 18:58:26 +0800 Subject: [PATCH 13/29] APK 15.4.16 release Publish. --- powerbell/build.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 52f43357..59412900 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 10:33:32 GMT 2025 -stageCount=16 +#Wed Oct 22 18:58:26 HKT 2025 +stageCount=17 libraryProject= baseVersion=15.4 -publishVersion=15.4.15 -buildCount=2 -baseBetaVersion=15.4.16 +publishVersion=15.4.16 +buildCount=0 +baseBetaVersion=15.4.17 From d7a9cb2a20920ff4a1fc6b74a5882de28f033510 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 20:16:26 +0800 Subject: [PATCH 14/29] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E7=94=B5=E9=87=8F=E6=B6=88=E8=80=97=E8=AE=A1=E7=AE=97=E4=B8=8D?= =?UTF-8?q?=E5=87=86=E7=A1=AE=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../activities/BatteryReportActivity.java | 275 +++++++++--------- 2 files changed, 145 insertions(+), 134 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index 59412900..dd1821ab 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 18:58:26 HKT 2025 +#Wed Oct 22 12:10:38 GMT 2025 stageCount=17 libraryProject= baseVersion=15.4 publishVersion=15.4.16 -buildCount=0 +buildCount=3 baseBetaVersion=15.4.17 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 index 7a6bf84b..647bf642 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BatteryReportActivity.java @@ -39,7 +39,6 @@ public class BatteryReportActivity extends Activity { private RecyclerView rvBatteryReport; private BatteryReportAdapter adapter; - // 数据列表:仅存储包名、耗电、时长(不存储名称,计算全程用包名) private List dataList = new ArrayList(); private List filteredList = new ArrayList(); private BroadcastReceiver batteryReceiver; @@ -47,18 +46,14 @@ public class BatteryReportActivity extends Activity { private float lastBatteryPercent = 100.0f; private long lastCheckTime = System.currentTimeMillis(); private EditText etSearch; - // 应用运行时长缓存(key:包名,value:时长ms) private Map appRunTimeCache = new HashMap(); - // 新增:包名-应用名称映射缓存(用于搜索匹配名称,避免重复查询) private Map packageToAppNameCache = new HashMap(); - // 包管理工具(全局持有,避免重复获取) private PackageManager mPackageManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_battery_report); - // 初始化包管理工具(仅一次,供后续查询名称使用) mPackageManager = getPackageManager(); // 权限检查(Java7 传统条件判断) @@ -72,34 +67,31 @@ public class BatteryReportActivity extends Activity { rvBatteryReport = (RecyclerView) findViewById(R.id.rv_battery_report); rvBatteryReport.setLayoutManager(new LinearLayoutManager(this)); - // 1. 加载所有应用(仅存包名,不查名称) + // 初始化流程:新增“加载24小时累计耗电”步骤 loadAllAppPackage(); - // 2. 预缓存所有包名对应的应用名称(为搜索名称做准备) preCacheAllAppNames(); - // 4. 首次获取运行时长(key:包名) appRunTimeCache = getAppRunTime(); - // 5. 更新时长到数据模型(用包名匹配) updateAppRunTimeToModel(); - // 6. 初始化过滤列表和适配器(传入包管理工具+名称缓存) + 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()); // 替换为新的双维度过滤方法 + filterAppsByPackageAndName(s.toString()); } @Override public void afterTextChanged(Editable s) {} }); - // 电池广播(耗电计算全程用包名) + // 电池广播:调用修改后的“单次耗电计算+累计累加”方法 batteryReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -108,17 +100,13 @@ public class BatteryReportActivity extends Activity { 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"); - // 重新获取最新运行时长(key:包名) appRunTimeCache = getAppRunTime(); - // 更新模型中的运行时长(包名匹配) updateAppRunTimeToModel(); - // 分配耗电(包名计算) - distributeBatteryConsumption(dropPercent, appRunTimeCache); + calculateSingleConsumptionAndAccumulate(dropPercent, appRunTimeCache); // 单次+累计逻辑 } lastBatteryPercent = currentPercent; @@ -138,67 +126,63 @@ public class BatteryReportActivity extends Activity { } /** - * 加载所有应用(仅获取包名,不查询应用名称,数据计算核心步骤) + * 加载所有应用(仅获取包名,初始化模型时单次耗电、累计耗电均设为0) */ private void loadAllAppPackage() { List appList = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA); - dataList.clear(); // 清空列表避免重复 + dataList.clear(); LogUtils.d(TAG, "开始加载应用包名列表,共找到" + appList.size() + "个应用"); - // 仅遍历包名,不处理名称(避免名称获取异常影响) for (ApplicationInfo appInfo : appList) { String packageName = appInfo.packageName; - // 模型仅存包名、初始耗电0、初始时长0(名称显示/搜索时用缓存) - dataList.add(new AppBatteryModel(packageName, "0.0", 0)); + // 初始化:单次耗电(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(); // 清空旧缓存 + packageToAppNameCache.clear(); LogUtils.d(TAG, "开始预缓存包名-应用名称映射"); for (AppBatteryModel model : dataList) { String packageName = model.getPackageName(); - String appName = getAppNameByPackage(packageName); // 复用工具方法获取名称 - packageToAppNameCache.put(packageName, appName); // 缓存映射关系 + 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; - // Java7 显式判断包名是否在缓存中 if (appRunTimeCache.containsKey(packageName)) { runTime = appRunTimeCache.get(packageName); LogUtils.d(TAG, String.format("应用包 %s 运行时长已更新。", packageName)); @@ -213,30 +197,94 @@ public class BatteryReportActivity extends Activity { } /** - * 新增:双维度过滤(包名+应用名称) - * 搜索关键词同时匹配“包名(不区分大小写)”或“应用名称(不区分大小写)” + * 【新增】初始化时计算24小时累计耗电(赋值给totalConsumption) + * 逻辑:基于24小时运行时长占比,分配当前电池容量的理论24小时消耗 + */ + private void calculateInitial24hTotalConsumption() { + long total24hRunTime = 0; + // 1. 计算24小时内所有应用总运行时长 + for (Map.Entry 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 runTimeMap) { + long totalSingleRunTime = 0; + // 1. 计算本次电池下降期间的总运行时长 + for (Map.Entry 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() { + @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(); // 统一转为小写,实现不区分大小写搜索 + String lowerKeyword = keyword.toLowerCase(); for (AppBatteryModel model : dataList) { String packageName = model.getPackageName(); String packageNameLower = packageName.toLowerCase(); - // 从缓存中获取应用名称(避免重复查询PackageManager) String appName = packageToAppNameCache.get(packageName); String appNameLower = appName.toLowerCase(); - // 匹配规则:包名包含关键词 OR 应用名称包含关键词 boolean isMatched = packageNameLower.contains(lowerKeyword) - || appNameLower.contains(lowerKeyword); + || appNameLower.contains(lowerKeyword); if (isMatched) { filteredList.add(model); - } } } @@ -244,7 +292,7 @@ public class BatteryReportActivity extends Activity { } /** - * 获取应用运行时长(返回包名-时长映射,不处理名称) + * 获取应用运行时长(逻辑不变,返回24小时运行时长) */ private Map getAppRunTime() { Map runTimeMap = new HashMap(); @@ -257,11 +305,10 @@ public class BatteryReportActivity extends Activity { List 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 + "运行时长:" + formatRunTime(runTimeMs)); + 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)); @@ -277,7 +324,7 @@ public class BatteryReportActivity extends Activity { } /** - * 格式化运行时长(工具方法,与包名无关) + * 格式化运行时长(逻辑不变) */ private String formatRunTime(long runTimeMs) { if (runTimeMs <= 0) { @@ -298,48 +345,7 @@ public class BatteryReportActivity extends Activity { } /** - * 分配电池消耗(全程用包名计算,不涉及名称) - */ - private void distributeBatteryConsumption(float totalDropPercent, Map runTimeMap) { - long totalRunTime = 0; - // 遍历包名计算总时长 - for (Map.Entry entry : runTimeMap.entrySet()) { - totalRunTime += entry.getValue(); - } - - // 按包名匹配计算各应用耗电 - for (AppBatteryModel model : dataList) { - String packageName = model.getPackageName(); - Long appRunTime; - // Java7 显式判断包名是否存在 - if (runTimeMap.containsKey(packageName)) { - appRunTime = runTimeMap.get(packageName); - } else { - appRunTime = 0L; - } - - float ratio = (totalRunTime > 0) ? (float) appRunTime / totalRunTime : 0; - float consumption = batteryCapacity * totalDropPercent / 100 * ratio; - model.setConsumption(String.format("%.1f", consumption)); - model.setRunTime(appRunTime); // 同步更新时长 - } - - // 按耗电排序(用包名对应的耗电值排序) - 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); // 逆序(耗电从高到低) - } - }); - - // 重新应用“包名+名称”双维度过滤 - filterAppsByPackageAndName(etSearch.getText().toString()); - } - - /** - * 权限检查(仅操作系统服务,与包名无关) + * 权限检查(逻辑不变) */ private boolean hasUsageStatsPermission(Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { @@ -361,31 +367,43 @@ public class BatteryReportActivity extends Activity { } /** - * 数据模型(仅存储包名、耗电、时长,不存名称) + * 【核心修改】数据模型:明确字段含义 + * - consumption:单次耗电(两次电池广播间的消耗,float类型便于计算) + * - totalConsumption:累计耗电(24小时初始化值+后续单次累加,显示用) */ public static class AppBatteryModel { - private String packageName; // 核心标识:应用包名 - private String consumption; // 电量消耗(mAh) - private long runTime; // 运行时长(ms) + private String packageName; // 应用包名(核心标识) + private float consumption; // 单次耗电(mAh,float类型) + private float totalConsumption;// 累计耗电(mAh,显示+排序用) + private long runTime; // 运行时长(ms) - // Java7 显式构造(仅传入包名、初始耗电、初始时长) - public AppBatteryModel(String packageName, String consumption, long runTime) { + // Java7 显式构造:初始化单次耗电、累计耗电为0 + public AppBatteryModel(String packageName, float consumption, float totalConsumption, long runTime) { this.packageName = packageName; - this.consumption = consumption; + this.consumption = consumption; // 单次耗电初始为0 + this.totalConsumption = totalConsumption; // 累计耗电初始为0(后续初始化时赋值) this.runTime = runTime; } - // Getter/Setter(仅操作包名、耗电、时长) + // Getter/Setter:覆盖所有字段,确保数据操作正常 public String getPackageName() { return packageName; } - public String getConsumption() { - return consumption; + public float getConsumption() { + return consumption; // 获取单次耗电 } - public void setConsumption(String consumption) { - this.consumption = 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() { @@ -398,37 +416,37 @@ public class BatteryReportActivity extends Activity { } /** - * RecyclerView 适配器(支持从缓存获取名称,避免重复查询) + * RecyclerView 适配器:仅显示累计耗电(totalConsumption),逻辑适配模型修改 */ public static class BatteryReportAdapter extends RecyclerView.Adapter { private Context mContext; private List mDataList; - private PackageManager mPm; // 兜底用:缓存无名称时查询 - private Map mPackageToNameCache; // 新增:名称缓存映射 + private PackageManager mPm; + private Map mPackageToNameCache; - // Java7 显式构造(新增名称缓存参数) + // Java7 显式构造:接收名称缓存,确保显示时高效获取应用名 public BatteryReportAdapter(Context context, List dataList, PackageManager pm, Map packageToNameCache) { this.mContext = context; this.mDataList = dataList; this.mPm = pm; - this.mPackageToNameCache = packageToNameCache; // 接收名称缓存 + 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 显式非空判断(避免空指针) + // Java7 显式非空判断:避免空指针异常 if (mDataList == null || mDataList.isEmpty() || position >= mDataList.size()) { holder.tvAppName.setText("未知应用"); - holder.tvConsumption.setText("耗电:0.0 mAh | 运行时长:0秒"); + holder.tvConsumption.setText("累计耗电:0.0 mAh | 运行时长:0秒"); return; } @@ -436,66 +454,59 @@ public class BatteryReportActivity extends Activity { String packageName = model.getPackageName(); String appName = ""; - // 优先从缓存获取名称(避免重复调用PackageManager,提升性能) + // 优先从缓存获取应用名:减少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; + appName = packageName; // 包名不存在时用包名兜底 LogUtils.e("Adapter", "包名" + packageName + "对应的应用未找到:" + e.getMessage()); } catch (Exception e) { - appName = packageName; + appName = packageName; // 其他异常时用包名兜底 LogUtils.e("Adapter", "查询应用名称失败(包名:" + packageName + "):" + e.getMessage()); } } - // 显示应用名称(缓存/兜底结果) + // 显示逻辑:仅展示累计耗电(totalConsumption),隐藏单次耗电 holder.tvAppName.setText(appName); - - // 格式化运行时长(调用 Activity 的工具方法) + // 格式化运行时长 + 累计耗电(保留1位小数,提升可读性) String runTimeStr = ((BatteryReportActivity) mContext).formatRunTime(model.getRunTime()); - // 显示耗电+运行时长(基于包名计算的结果) - String consumptionText = String.format("耗电:%s mAh | 运行时长:%s", - model.getConsumption(), runTimeStr); - holder.tvConsumption.setText(consumptionText); + 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.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); - holder.tvConsumption.setTextColor(mContext.getResources().getColor(android.R.color.holo_blue_dark)); - - //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 三元运算符) + // 获取列表长度:Java7 三元运算符判断空值,避免空指针 @Override public int getItemCount() { return mDataList == null ? 0 : mDataList.size(); } - // ViewHolder(绑定系统布局的两个 TextView) + /** + * ViewHolder:绑定系统布局控件,与显示逻辑对应 + */ public static class ViewHolder extends RecyclerView.ViewHolder { - TextView tvAppName; // 显示应用名称(缓存/兜底获取) - TextView tvConsumption; // 显示耗电+运行时长(包名计算结果) + TextView tvAppName; // 显示应用名称 + TextView tvConsumption; // 显示累计耗电 + 运行时长 - // Java7 显式构造 + // Java7 显式构造:绑定控件ID(系统布局固定ID:text1、text2) public ViewHolder(View itemView) { super(itemView); - // 强制类型转换(系统布局控件 ID:text1 和 text2) tvAppName = (TextView) itemView.findViewById(android.R.id.text1); tvConsumption = (TextView) itemView.findViewById(android.R.id.text2); } From f943db17e0fcf6e4669f9088eee9a35e5d2f71f1 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 22 Oct 2025 20:17:00 +0800 Subject: [PATCH 15/29] APK 15.4.17 release Publish. --- powerbell/build.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index dd1821ab..0b19fcc5 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 12:10:38 GMT 2025 -stageCount=17 +#Wed Oct 22 20:17:00 HKT 2025 +stageCount=18 libraryProject= baseVersion=15.4 -publishVersion=15.4.16 -buildCount=3 -baseBetaVersion=15.4.17 +publishVersion=15.4.17 +buildCount=0 +baseBetaVersion=15.4.18 From 5a77a5e9e00101af60ffbade386617b39b7b948a Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Fri, 14 Nov 2025 11:45:35 +0800 Subject: [PATCH 16/29] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A8=AA=E5=B9=85?= =?UTF-8?q?=E5=B9=BF=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.gradle | 64 ++-- powerbell/build.properties | 4 +- powerbell/src/main/AndroidManifest.xml | 22 +- .../java/cc/winboll/studio/powerbell/App.java | 12 +- .../studio/powerbell/MainActivity.java | 347 +++++++++++++++++- .../powerbell/activities/AboutActivity.java | 4 +- .../activities/BackgroundPictureActivity.java | 2 +- .../activities/ClearRecordActivity.java | 4 +- .../activities/PixelPickerActivity.java | 15 +- .../powerbell/activities/WinBoLLActivity.java | 100 ++++- .../services/ControlCenterService.java | 24 +- .../studio/powerbell/utils/MimoUtils.java | 33 ++ .../src/main/res/layout/activity_main.xml | 41 +++ .../main/res/xml/network_security_config.xml | 11 + 14 files changed, 616 insertions(+), 67 deletions(-) create mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/utils/MimoUtils.java create mode 100644 powerbell/src/main/res/xml/network_security_config.xml diff --git a/powerbell/build.gradle b/powerbell/build.gradle index f4810637..45053547 100644 --- a/powerbell/build.gradle +++ b/powerbell/build.gradle @@ -52,17 +52,41 @@ android { includeInApk = false includeInBundle = false } + + buildFeatures { + dataBinding true + } + + lintOptions { + abortOnError false + } +} + +repositories { + flatDir { + dirs 'libs' + } } dependencies { - api fileTree(dir: 'libs', include: ['*.jar']) - api 'cc.winboll.studio:libaes:15.9.3' - api 'cc.winboll.studio:libapputils:15.8.5' - api 'cc.winboll.studio:libappbase:15.9.5' - - // 吐司提示库 - api 'com.github.getActivity:ToastUtils:10.5' - // 应用介绍页类库 + + //--------------aar依赖----------- + implementation(name: 'mimo-ad-sdk', ext: 'aar') + //--------------Maven依赖----------- + //implementation 'com.miui.zeus:mimo-ad-sdk:5.3.2' + + //注意:以下5个库必须要引入 + implementation "androidx.appcompat:appcompat:1.2.0" + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'com.github.bumptech.glide:glide:4.9.0' + //annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' + + implementation "androidx.navigation:navigation-ui:2.3.5" + implementation 'androidx.multidex:multidex:2.0.1' + //debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' + + // 应用介绍页类库 api 'io.github.medyo:android-about-page:2.0.0' // SSH api 'com.jcraft:jsch:0.1.55' @@ -82,23 +106,9 @@ dependencies { //api 'androidx.vectordrawable:vectordrawable-animated:1.1.0' //api 'androidx.fragment:fragment:1.1.0' - - /*api 'cc.winboll.studio:winboll-shared:1.8.0' - api 'io.github.medyo:android-about-page:2.0.0' - api 'com.jcraft:jsch:0.1.55' - api 'org.jsoup:jsoup:1.13.1' - api 'com.squareup.okhttp3:okhttp:4.4.1' - - api 'androidx.appcompat:appcompat:1.0.0' - api 'androidx.fragment:fragment:1.0.0' - api 'com.google.android.material:material:1.0.0' - - api 'com.baoyz.pullrefreshlayout:library:1.2.0' - api 'com.github.getActivity:ToastUtils:10.5' - api 'io.github.medyo:android-about-page:2.0.0' - api 'org.jsoup:jsoup:1.13.1' - api 'com.squareup.okhttp3:okhttp:4.4.1' - - api 'cc.winboll.studio:libaes:7.6.0' - */ + implementation 'cc.winboll.studio:libaes:15.11.0' + implementation 'cc.winboll.studio:libappbase:15.11.0' + + //api fileTree(dir: 'libs', include: ['*.aar']) + api fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/powerbell/build.properties b/powerbell/build.properties index 0b19fcc5..b7a98f95 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Oct 22 20:17:00 HKT 2025 +#Fri Nov 14 03:40:03 GMT 2025 stageCount=18 libraryProject= baseVersion=15.4 publishVersion=15.4.17 -buildCount=0 +buildCount=11 baseBetaVersion=15.4.18 diff --git a/powerbell/src/main/AndroidManifest.xml b/powerbell/src/main/AndroidManifest.xml index e5940409..ab085daf 100644 --- a/powerbell/src/main/AndroidManifest.xml +++ b/powerbell/src/main/AndroidManifest.xml @@ -4,6 +4,19 @@ xmlns:tools="http://schemas.android.com/tools" package="cc.winboll.studio.powerbell"> + + + + + + + + + + + + @@ -36,7 +49,7 @@ - + @@ -47,7 +60,7 @@ - + + android:requestLegacyExternalStorage="true" + android:usesCleartextTraffic="true" + tools:ignore="GoogleAppIndexingWarning" + android:networkSecurityConfig="@xml/network_security_config"> mAllBanners = new ArrayList<>(); + + private ViewGroup mContainer; + + private Button mFetchBtn; + private Button mShowBtn; + + private boolean mIsBiddingWin = true; + public static final int BACKGROUND_PICTURE_REQUEST_CODE = 0; public static MainActivity _mMainActivity; @@ -54,6 +122,27 @@ public class MainActivity extends WinBoLLActivity { //LogUtils.d(TAG, "onCreate(...)"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + + // 米盟模块 + showPrivacy(); + + mContainer = findViewById(R.id.container); + mFetchBtn = findViewById(R.id.fetchAd); + mShowBtn = findViewById(R.id.showAd); + + mFetchBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + fetchAd(); + } + }); + + mShowBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showAd(); + } + }); // 设置调试日志 // mLogView = findViewById(R.id.logview); @@ -85,6 +174,20 @@ public class MainActivity extends WinBoLLActivity { } + @Override + protected void onDestroy() { + super.onDestroy(); + try { + for(BannerAd ad: mAllBanners){ + ad.destroy(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + void showFragment(Fragment fragment) { FragmentTransaction tx = getFragmentManager().beginTransaction(); for (Fragment item : getFragmentManager().getFragments()) { @@ -172,7 +275,7 @@ public class MainActivity extends WinBoLLActivity { intent.setClass(this, BackgroundPictureActivity.class); startActivity(intent); } else if (menuItemId == R.id.action_log) { - App.getWinBoLLActivityManager().startLogActivity(this); + LogActivity.startLogActivity(this); } return true; @@ -217,4 +320,246 @@ public class MainActivity extends WinBoLLActivity { RelativeLayout mainLayout = findViewById(R.id.activitymainRelativeLayout1); mainLayout.setBackgroundColor(nPixelColor); } + + private void showAd() { + final FrameLayout container = new FrameLayout(this); + container.setPadding(0, 0, 0, MimoUtils.dpToPx(this, 10)); + mContainer.addView(container, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)); + + if (mIsBiddingWin){ + //如果竞价成功,传⼊本次实际结算价 + mBannerAd.setPrice(getPrice()); + } + mBannerAd.showAd(this, container, new BannerAd.BannerInteractionListener() { + @Override + public void onAdClick() { + Log.d(TAG, "onAdClick"); + } + + @Override + public void onAdShow() { + mShowBtn.setEnabled(false); + Log.d(TAG, "onAdShow"); + } + + @Override + public void onAdDismiss() { + Log.d(TAG, "onAdDismiss"); + mContainer.removeView(container); + } + + @Override + public void onRenderSuccess() { + Log.d(TAG, "onRenderSuccess"); + } + + @Override + public void onRenderFail(int code, String msg) { + Log.e(TAG, "onRenderFail errorCode " + code + " errorMsg " + msg); + } + }); + } + + private void fetchAd() { + mShowBtn.setEnabled(false); + mBannerAd = new BannerAd(); + mAllBanners.add(mBannerAd); + mBannerAd.setDownLoadListener(new BannerAd.BannerDownloadListener() { + @Override + public void onDownloadStarted() { + Log.e(TAG, "onDownloadStarted"); + } + + @Override + public void onDownloadPaused() { + Log.e(TAG, "onDownloadPaused"); + } + + @Override + public void onDownloadFailed(int errorCode) { + Log.e(TAG, "onDownloadFailed, errorCode = "+ errorCode); + } + + @Override + public void onDownloadFinished() { + Log.e(TAG, "onDownloadFinished"); + } + + @Override + public void onDownloadProgressUpdated(int progress) { + Log.e(TAG, "onDownloadProgressUpdated "+ progress + "%"); + } + + @Override + public void onInstallFailed(int errorCode) { + Log.e(TAG, "onInstallFailed, errorCode = "+ errorCode); + } + + @Override + public void onInstallStart() { + Log.e(TAG, "onInstallStart"); + } + + @Override + public void onInstallSuccess() { + Log.e(TAG, "onInstallSuccess"); + } + + @Override + public void onDownloadCancel() { + Log.e(TAG, "onDownloadCancel"); + } + }); + ADParams params = new ADParams.Builder().setUpId(BANNER_POS_ID).build(); + mBannerAd.loadAd(params, new BannerAd.BannerLoadListener() { + @Override + public void onBannerAdLoadSuccess() { + mShowBtn.setEnabled(true); + + long bannerPrice = getPrice(); + if (mIsBiddingWin){ + //竞价成功时候上报win + Map auctionBidInfo = new HashMap<>(); + auctionBidInfo.put(BaseAd.IBidding.EXPECT_COST_PRICE, bannerPrice); + auctionBidInfo.put(BaseAd.IBidding.HIGHEST_LOSS_PRICE, BID_WIN_HIGHEST_LOSS_PRICE); + mBannerAd.win(auctionBidInfo); + } else { + //竞价失败的时候上报loss + Map lossReasonInfo = new HashMap<>(); + lossReasonInfo.put(BaseAd.IBidding.WIN_PRICE, BID_LOSS_WIN_PRICE); + lossReasonInfo.put(BaseAd.IBidding.LOSS_REASON, BaseAd.LossReason.TYPE_LOWER_OTHER_BIDDER_PRICE); + lossReasonInfo.put(BaseAd.IBidding.ADN_ID, 1); + mBannerAd.loss(lossReasonInfo); + } + } + + @Override + public void onAdLoadFailed(int errorCode, String errorMsg) { + Log.e(TAG, "errorCode = " + errorCode + ", errorMsg = " + errorMsg); + } + }); + } + + private long getPrice(){ + if (mBannerAd == null){ + return 0; + } + Map map = mBannerAd.getMediaExtraInfo(); + if (map == null || map.isEmpty()){ + return 0; + } + return (long) map.get("price"); + } + + + private void showPrivacy(){ + String privacyAgreeValue = getSharedPreferences().getString(PRIVACY_VALUE, null); + if (TextUtils.equals(privacyAgreeValue, String.valueOf(0))){ + Log.i(TAG, "已拒绝隐私协议,广告已处于不可以状态..."); + Toast.makeText(this, "已拒绝隐私协议,广告已处于不可以状态", Toast.LENGTH_SHORT).show(); + return; + } + if (TextUtils.equals(privacyAgreeValue, String.valueOf(1))){ + Log.i(TAG, "已同意隐私协议,开始初始化..."); + initMimoSdk(); + return; + } + Log.i(TAG, "开始弹出隐私协议..."); + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setTitle("用户须知"); + builder.setMessage("小米广告SDK隐私政策: https://dev.mi.com/distribute/doc/details?pId=1688, 请复制到浏览器查看"); + builder.setIcon(R.drawable.ic_launcher); + builder.setCancelable(false); //点击对话框以外的区域是否让对话框消失 + builder.setPositiveButton("同意", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences.Editor editor = getSharedPreferences().edit(); + editor.putString(PRIVACY_VALUE, String.valueOf(1)); + editor.apply(); + initMimoSdk(); + dialog.dismiss(); + } + }); + builder.setNegativeButton("拒绝", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences.Editor editor = getSharedPreferences().edit(); + editor.putString(PRIVACY_VALUE, String.valueOf(0)); + editor.apply(); + dialog.dismiss(); + } + }); + AlertDialog dialog = builder.create(); + + Window window = dialog.getWindow(); + window.setGravity(Gravity.BOTTOM); + + WindowManager m = getWindowManager(); + Display d = m.getDefaultDisplay(); + WindowManager.LayoutParams p = dialog.getWindow().getAttributes(); + p.width = d.getWidth(); + dialog.getWindow().setAttributes(p); + dialog.show(); + } + + private void initMimoSdk() { + MimoSdk.init(this, new MimoCustomController() { + + @Override + public boolean isCanUseLocation() { + return true; + } + + @Override + public MimoLocation getMimoLocation() { + return null; + } + + @Override + public boolean isCanUseWifiState() { + return true; + } + + @Override + public boolean alist() { + return true; + } + } , new MimoSdk.InitCallback() { + @Override + public void success() { + Log.d(TAG, "MimoSdk init success"); + } + + @Override + public void fail(int code, String msg) { + Log.e(TAG, "MimoSdk init fail, code=" + code + ",msg=" + msg); + } + }); + MimoSdk.setDebugOn(true); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return super.dispatchKeyEvent(event); + } + +// @Override +// public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { +// return false; +// } + + @Override + public void setupToolbar() { + super.setupToolbar(); + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + } + } + + public SharedPreferences getSharedPreferences() { + if (mSharedPreferences == null){ + mSharedPreferences = getSharedPreferences(PRIVACY_FILE, Context.MODE_PRIVATE); + } + return mSharedPreferences; + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/AboutActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/AboutActivity.java index 9f54ad02..63ea7804 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/AboutActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/AboutActivity.java @@ -10,9 +10,9 @@ import android.content.Context; import android.os.Bundle; import android.view.ViewGroup; import android.widget.LinearLayout; +import cc.winboll.studio.libaes.models.APPInfo; import cc.winboll.studio.libaes.views.AToolbar; -import cc.winboll.studio.libaes.winboll.APPInfo; -import cc.winboll.studio.libaes.winboll.AboutView; +import cc.winboll.studio.libaes.views.AboutView; import cc.winboll.studio.powerbell.R; public class AboutActivity extends Activity { diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundPictureActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundPictureActivity.java index f29a768b..9bf3d440 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundPictureActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundPictureActivity.java @@ -19,7 +19,7 @@ import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import cc.winboll.studio.libaes.views.AToolbar; import cc.winboll.studio.libappbase.LogUtils; -import cc.winboll.studio.libappbase.utils.ToastUtils; +import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.beans.BackgroundPictureBean; diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ClearRecordActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ClearRecordActivity.java index d53e7671..99e4d7f0 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ClearRecordActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ClearRecordActivity.java @@ -4,11 +4,12 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; +import android.widget.Switch; import android.widget.TextView; import cc.winboll.studio.libaes.views.AOHPCTCSeekBar; import cc.winboll.studio.libaes.views.AToolbar; import cc.winboll.studio.libappbase.LogUtils; -import cc.winboll.studio.libappbase.utils.ToastUtils; +import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.beans.BatteryInfoBean; @@ -16,7 +17,6 @@ import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver; import cc.winboll.studio.powerbell.utils.AppCacheUtils; import cc.winboll.studio.powerbell.utils.StringUtils; import java.util.ArrayList; -import android.widget.Switch; public class ClearRecordActivity extends Activity { diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java index a781a888..85fd970c 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java @@ -6,6 +6,7 @@ package cc.winboll.studio.powerbell.activities; */ import android.app.Activity; import android.app.Dialog; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; @@ -19,11 +20,11 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libaes.views.AToolbar; import cc.winboll.studio.libappbase.GlobalApplication; -import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; -import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.R; +import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity; import cc.winboll.studio.powerbell.activities.PixelPickerActivity; import cc.winboll.studio.powerbell.beans.BackgroundPictureBean; import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils; @@ -233,7 +234,10 @@ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActi @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { - GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class); + Intent intent = new Intent(); + intent.setClass(this, BackgroundPictureActivity.class); + startActivity(intent); + //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), ); return true; } // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 @@ -243,7 +247,10 @@ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActi @Override public void onBackPressed() { super.onBackPressed(); - GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class); + Intent intent = new Intent(); + intent.setClass(this, BackgroundPictureActivity.class); + startActivity(intent); + //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java index e44b8257..035374b3 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java @@ -5,40 +5,99 @@ package cc.winboll.studio.powerbell.activities; * @Date 2025/06/19 20:35 * @Describe 应用窗口基类 */ -import android.app.Activity; import android.os.Bundle; import android.view.MenuItem; -import cc.winboll.studio.libappbase.GlobalApplication; -import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity; -import cc.winboll.studio.powerbell.App; -import cc.winboll.studio.powerbell.MainActivity; +import androidx.appcompat.app.AppCompatActivity; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import com.miui.zeus.mimo.sdk.BuildConfig; import cc.winboll.studio.powerbell.R; -public abstract class WinBoLLActivity extends Activity implements IWinBoLLActivity { +@SuppressLint("SetTextI18n") +public abstract class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { public static final String TAG = "WinBoLLActivity"; - + + protected TextView mTagView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + changeFullScreen(this); + } + + + @Override + protected void onStart() { + super.onStart(); + addVersionNameToContentView(); + } + + protected void addVersionNameToContentView() { + if (!isTagViewVisible()) { + return; + } + if (mTagView == null) { + mTagView = new TextView(this); + mTagView.setTextColor(Color.GRAY); + mTagView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); + mTagView.setText("MIMO SDK V" + BuildConfig.VERSION_NAME); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + FrameLayout frameLayout = findViewById(android.R.id.content); + frameLayout.addView(mTagView, params); + } + } + + protected boolean isTagViewVisible() { + return true; + } + + public void setupToolbar() { +// mToolbar = findViewById(R.id.toolbar); +// if (mToolbar != null) { +// setSupportActionBar(mToolbar); +// if (getSupportActionBar() != null) { +// getSupportActionBar().setDisplayHomeAsUpEnabled(true); +// } +// } } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - GlobalApplication.getWinBoLLActivityManager().add(this); + //GlobalApplication.getWinBoLLActivityManager().add(this); } @Override protected void onDestroy() { super.onDestroy(); - GlobalApplication.getWinBoLLActivityManager().registeRemove(this); + //GlobalApplication.getWinBoLLActivityManager().registeRemove(this); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { - GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class); + //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class); return true; } // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 @@ -48,6 +107,25 @@ public abstract class WinBoLLActivity extends Activity implements IWinBoLLActivi @Override public void onBackPressed() { super.onBackPressed(); - GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class); + //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class); } + + public void changeFullScreen(Activity activity) { + Window window = activity.getWindow(); + if (window == null){ + return; + } + View decorView = window.getDecorView(); + if (decorView == null){ + return; + } + int flag = decorView.getSystemUiVisibility(); + flag |= View.SYSTEM_UI_FLAG_FULLSCREEN; + flag |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + flag |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + flag |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + decorView.setSystemUiVisibility(flag); + window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java index d0730712..955d650b 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java @@ -14,10 +14,12 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.widget.RemoteViews; import cc.winboll.studio.libappbase.LogUtils; -import cc.winboll.studio.libappbase.utils.ToastUtils; +import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.R; @@ -32,8 +34,6 @@ import cc.winboll.studio.powerbell.utils.AppConfigUtils; import cc.winboll.studio.powerbell.utils.NotificationHelper; import cc.winboll.studio.powerbell.utils.ServiceUtils; import cc.winboll.studio.powerbell.utils.StringUtils; -import android.os.Handler; -import android.os.Looper; public class ControlCenterService extends Service { @@ -73,8 +73,8 @@ public class ControlCenterService extends Service { mAppConfigUtils = App.getAppConfigUtils(this); mAppCacheUtils = App.getAppCacheUtils(this); mNotificationHelper = new NotificationHelper(ControlCenterService.this); - - + + if (mMyServiceConnection == null) { mMyServiceConnection = new MyServiceConnection(); } @@ -105,7 +105,7 @@ public class ControlCenterService extends Service { Intent intent = new Intent(this, MainActivity.class); notification = helper.showForegroundNotification(intent, getString(R.string.app_name), "Service Running, Click to open app"); startForeground(NotificationHelper.FOREGROUND_NOTIFICATION_ID, notification); - + // NotificationMessage notificationMessage=createNotificationMessage(); // //Toast.makeText(getApplication(), "", Toast.LENGTH_SHORT).show(); // mNotificationUtils.createForegroundNotification(this, notificationMessage); @@ -116,7 +116,7 @@ public class ControlCenterService extends Service { mControlCenterServiceReceiver = new ControlCenterServiceReceiver(this); mControlCenterServiceReceiver.registerAction(this); } - + new Handler(Looper.getMainLooper()).postDelayed(new Runnable(){ @Override @@ -126,7 +126,7 @@ public class ControlCenterService extends Service { LogUtils.i(TAG, "Service Is Start."); } }, 2000); - + } } @@ -263,15 +263,15 @@ public class ControlCenterService extends Service { NotificationHelper helper = new NotificationHelper(ControlCenterService.this); Intent intent = new Intent(ControlCenterService.this, MainActivity.class); helper.showTemporaryNotification(intent, getString(R.string.app_name), msg); - - - + + + // NotificationMessage notificationMessage = createNotificationMessage(); // notificationMessage.setRemindMSG(szRemindMSG); // //LogUtils.d(TAG, "notificationMessage : " + notificationMessage.getRemindMSG()); // updateRemindNotification(notificationMessage); } - + // 设置颜色背景 public static RemoteViews setLinearLayoutColor(RemoteViews remoteViews, int viewId, int color) { remoteViews.setInt(viewId, "setBackgroundColor", color); diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/MimoUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/MimoUtils.java new file mode 100644 index 00000000..28038ca4 --- /dev/null +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/MimoUtils.java @@ -0,0 +1,33 @@ +package cc.winboll.studio.powerbell.utils; + +import android.content.Context; +import android.util.DisplayMetrics; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/14 11:14 + * @Describe 米盟 MimoUtils + */ +public final class MimoUtils { + public static final String TAG = "Utils"; + + public static int dpToPx(Context context, float dp) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (dp * displayMetrics.density + 0.5f); + } + + public static int pxToDp(Context context, float px) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (px / displayMetrics.density + 0.5f); + } + + public static int pxToSp(Context context, float pxValue) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (pxValue / displayMetrics.scaledDensity + 0.5f); + } + + public static int spToPx(Context context, float spValue) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + return (int) (spValue * displayMetrics.scaledDensity + 0.5f); + } +} diff --git a/powerbell/src/main/res/layout/activity_main.xml b/powerbell/src/main/res/layout/activity_main.xml index 54bf7525..c9c0457b 100644 --- a/powerbell/src/main/res/layout/activity_main.xml +++ b/powerbell/src/main/res/layout/activity_main.xml @@ -31,5 +31,46 @@ + + +