应用电量报告中添加应用名称搜索功能
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -51,6 +51,8 @@ public class BatteryReportActivity extends Activity {
|
||||
private EditText etSearch;
|
||||
// 应用运行时长缓存(key:包名,value:时长ms)
|
||||
private Map<String, Long> appRunTimeCache = new HashMap<String, Long>();
|
||||
// 新增:包名-应用名称映射缓存(用于搜索匹配名称,避免重复查询)
|
||||
private Map<String, String> packageToAppNameCache = new HashMap<String, String>();
|
||||
// 包管理工具(全局持有,避免重复获取)
|
||||
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<android.app.usage.UsageStats> 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<android.app.usage.UsageStats> 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<BatteryReportAdapter.ViewHolder> {
|
||||
private Context mContext;
|
||||
private List<AppBatteryModel> mDataList;
|
||||
private PackageManager mPm; // 用于显示时查询应用名称
|
||||
private PackageManager mPm; // 兜底用:缓存无名称时查询
|
||||
private Map<String, String> mPackageToNameCache; // 新增:名称缓存映射
|
||||
|
||||
// Java7 显式构造(传入包管理工具)
|
||||
public BatteryReportAdapter(Context context, List<AppBatteryModel> dataList, PackageManager pm) {
|
||||
// Java7 显式构造(新增名称缓存参数)
|
||||
public BatteryReportAdapter(Context context, List<AppBatteryModel> dataList,
|
||||
PackageManager pm, Map<String, String> 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 显式构造
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/white">
|
||||
|
||||
<!-- 搜索框:使用系统兼容背景 -->
|
||||
<!-- 搜索框:提示文本改为“搜索应用名称或包名” -->
|
||||
<EditText
|
||||
android:id="@+id/et_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:padding="12dp"
|
||||
android:hint="搜索应用包名"
|
||||
android:hint="搜索应用名称或包名"
|
||||
android:background="@android:drawable/btn_default_small"
|
||||
android:inputType="text"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<!-- 应用列表:不依赖系统分割线,避免资源缺失 -->
|
||||
<!-- 应用列表 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_battery_report"
|
||||
android:layout_width="match_parent"
|
||||
@@ -28,4 +28,3 @@
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user