Compare commits

...

10 Commits

Author SHA1 Message Date
4af10c74e9 <powerbell>APK 15.11.0 release Publish. 2025-11-16 13:12:25 +08:00
ZhanGSKen
7883ef93be 应用窗口恢复时,检查当前时间戳的奇偶性,来决定是否播放Demo自带广告还是播放WiBoLL广告。 2025-11-16 13:07:26 +08:00
ZhanGSKen
398cb33a58 示例广告加载完成 2025-11-16 09:53:58 +08:00
ZhanGSKen
ac1858fba7 米盟广告接入类库设置完成 2025-11-16 08:40:02 +08:00
ZhanGSKen
5a77a5e9e0 添加横幅广告 2025-11-14 11:45:35 +08:00
f943db17e0 <powerbell>APK 15.4.17 release Publish. 2025-10-22 20:17:00 +08:00
ZhanGSKen
d7a9cb2a20 修复应用电量消耗计算不准确的问题。 2025-10-22 20:16:26 +08:00
de34c33706 <powerbell>APK 15.4.16 release Publish. 2025-10-22 18:58:26 +08:00
ZhanGSKen
10b8da2e21 更新应用电量报告显示风格,精简冗余代码。 2025-10-22 18:36:16 +08:00
ZhanGSKen
ca4e4c7feb 更新源码识别命名 2025-10-22 18:26:54 +08:00
24 changed files with 925 additions and 287 deletions

View File

@@ -5,6 +5,16 @@ buildscript {
// 设置本地Maven仓库路径
url 'file:///sdcard/.m2/repository/'
}
//米盟通过maven接入时要做如下配置
maven {
url "https://repos.xiaomi.com/maven"
credentials {
username 'mimo-developer'
password 'AKCp8ih1PFG9tV8qaLyws67dLGZi8udFM39SfsHgihN15cgsiRvHuxj8JzFmuZjaViVeNawaA'
}
}
// Nexus Maven 库地址
// "WinBoLL Release"
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
@@ -40,7 +50,16 @@ allprojects {
// 设置本地Maven仓库路径
url 'file:///sdcard/.m2/repository/'
}
//米盟通过maven接入时要做如下配置
maven {
url "https://repos.xiaomi.com/maven"
credentials {
username 'mimo-developer'
password 'AKCp8ih1PFG9tV8qaLyws67dLGZi8udFM39SfsHgihN15cgsiRvHuxj8JzFmuZjaViVeNawaA'
}
}
// Nexus Maven 库地址
// "WinBoLL Release"
maven { url "https://nexus.winboll.cc/repository/maven-public/" }

View File

@@ -18,18 +18,22 @@ def genVersionName(def versionName){
}
android {
compileSdkVersion 32
buildToolsVersion "32.0.0"
// 1. compileSdkVersion必须 ≥ targetSdkVersion建议直接等于 targetSdkVersion30
compileSdkVersion 30
// 2. buildToolsVersion需匹配 compileSdkVersion建议使用 30.x.x 最新稳定版(无需高于 compileSdkVersion
buildToolsVersion "30.0.3" // 这是 30 对应的最新稳定版,避免使用 beta 版
defaultConfig {
applicationId "cc.winboll.studio.powerbell"
minSdkVersion 24
minSdkVersion 23
targetSdkVersion 30
versionCode 6
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "15.4"
versionName "15.11"
if(true) {
versionName = genVersionName("${versionName}")
}
@@ -42,27 +46,40 @@ android {
}
}
// 允许使用系统隐藏API
lintOptions {
checkReleaseBuilds false
abortOnError false
}
// 针对PowerProfile的依赖配置
dependenciesInfo {
includeInApk = false
includeInBundle = false
// 米盟
packagingOptions {
doNotStrip "*/*/libmimo_1011.so"
}
}
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'
// 应用介绍页类库
// 米盟
implementation 'com.miui.zeus:mimo-ad-sdk:5.3.+'//请使用最新版sdk
//注意以下5个库必须要引入
//implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.github.bumptech.glide:glide:4.9.0'
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
//--------------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 +99,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'])
}

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Oct 22 17:45:14 HKT 2025
stageCount=16
#Sun Nov 16 13:12:25 HKT 2025
stageCount=1
libraryProject=
baseVersion=15.4
publishVersion=15.4.15
baseVersion=15.11
publishVersion=15.11.0
buildCount=0
baseBetaVersion=15.4.16
baseBetaVersion=15.11.1

View File

@@ -15,3 +15,6 @@
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
## 米盟
-keep class com.miui.zeus.** { *; }

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">能源钟</string>
<string name="app_name">PowerBell</string>
</resources>

View File

@@ -3,6 +3,25 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="cc.winboll.studio.powerbell">
//米盟必要权限配置
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- 权限会用在部分下载类广告安装应用时使用 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
//以下为非必要权限配置可以不申请当您使用的米盟SDK版本大于等于5.3.1时可以选择性添加如下权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 米盟提供“获取地理位置权限”和“不给予地理位置权限,开发者传入地理位置参数”两种方式上报用户位置,两种方式均可不选,无论通过何种方式提供给米盟用户地理位置,均需向用户声明地理位置权限将应用于米盟广告投放,米盟不强制获取地理位置信息-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 米盟将通过此权限在Android 11系统上判定广告对应的应用是否在用户的app上安装避免投放错误的广告以此提高用户的广告体验。若添加此权限需要在您的用户隐私文档中声明 -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<!-- 通过GPS得到精确位置 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 通过网络得到粗略位置 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 拍摄照片和视频 -->
<uses-permission android:name="android.permission.CAMERA"/>
@@ -36,7 +55,7 @@
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<!-- 1. 基础应用信息读取权限Android 11 及以下) -->
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
@@ -47,7 +66,7 @@
<!-- 3. 可选:若需读取系统应用,添加此权限(部分机型需要) -->
<uses-permission android:name="android.permission.ACCESS_PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<application
android:name=".App"
android:allowBackup="true"
@@ -56,7 +75,10 @@
android:theme="@style/AppTheme_Default"
android:persistent="true"
android:resizeableActivity="true"
android:requestLegacyExternalStorage="true">
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"

View File

@@ -4,10 +4,10 @@ import android.content.Context;
import android.os.Environment;
import android.view.Gravity;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver;
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import com.hjq.toast.ToastUtils;
import java.io.File;
public class App extends GlobalApplication {
@@ -50,7 +50,7 @@ public class App extends GlobalApplication {
// 设置 Toast 布局样式
//ToastUtils.setView(R.layout.toast_custom_view);
//ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
//ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
// 设置数据配置存储工具
_mAppConfigUtils = getAppConfigUtils(this);
@@ -77,5 +77,13 @@ public class App extends GlobalApplication {
public void clearBatteryHistory() {
_mAppCacheUtils.clearBatteryHistory();
}
@Override
public void onTerminate() {
super.onTerminate();
ToastUtils.release();
}
}

View File

@@ -3,16 +3,30 @@ package cc.winboll.studio.powerbell;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import cc.winboll.studio.libaes.views.AToolbar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogActivity;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.activities.AboutActivity;
@@ -23,10 +37,35 @@ import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
import cc.winboll.studio.powerbell.utils.MimoUtils;
import com.miui.zeus.mimo.sdk.ADParams;
import com.miui.zeus.mimo.sdk.BannerAd;
import com.miui.zeus.mimo.sdk.MimoCustomController;
import com.miui.zeus.mimo.sdk.MimoLocation;
import com.miui.zeus.mimo.sdk.MimoSdk;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class MainActivity extends WinBoLLActivity {
public static final String TAG = "MainActivity";
private static final String PRIVACY_FILE = "privacy_pfs";
private static final String PRIVACY_VALUE = "privacy_value";//0: 拒绝1赞同
private SharedPreferences mSharedPreferences;
private String BANNER_POS_ID = "802e356f1726f9ff39c69308bfd6f06a";
private String BANNER_POS_ID_WINBOLL = "d129ee5a263911f981a6dc7a9802e3e7";
private BannerAd mBannerAd;
private List<BannerAd> mAllBanners = new ArrayList<>();
private ViewGroup mContainer;
private boolean mIsBiddingWin = true;
public static final int BACKGROUND_PICTURE_REQUEST_CODE = 0;
public static MainActivity _mMainActivity;
@@ -37,7 +76,7 @@ public class MainActivity extends WinBoLLActivity {
Menu mMenu;
Fragment mCurrentShowFragment;
MainViewFragment mMainViewFragment;
AToolbar mAToolbar;
Toolbar mToolbar;
@Override
public Activity getActivity() {
@@ -51,9 +90,14 @@ public class MainActivity extends WinBoLLActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//LogUtils.d(TAG, "onCreate(...)");
LogUtils.d(TAG, "onCreate(...)");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContainer = findViewById(R.id.ads_container);
// 米盟模块
showPrivacy();
// 设置调试日志
// mLogView = findViewById(R.id.logview);
@@ -66,10 +110,10 @@ public class MainActivity extends WinBoLLActivity {
//mAppConfigUtils = AppConfigUtils.getInstance(mApplication);
// 初始化工具栏
mAToolbar = (AToolbar) findViewById(R.id.toolbar);
setActionBar(mAToolbar);
mToolbar = findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
//mAToolbar.setSubtitle("Main");
mAToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
if (mMainViewFragment == null) {
FragmentTransaction tx = getFragmentManager().beginTransaction();
@@ -82,9 +126,143 @@ public class MainActivity extends WinBoLLActivity {
// NotificationHelper notificationUtils = new NotificationHelper(this);
// notificationUtils.createNotificationChannels();
}
// public void onADs(View view) {
// LogUtils.d(TAG, "onPostCreate");
// fetchAd();
//
// }
/*public void showADs() {
//(5.3.4新增接口) 请使用最新接口集成
ADParams params = new ADParams.Builder().setUpId(BANNER_POS_ID).build();
bannerAd.loadAd(params, new BannerAd.BannerLoadListener() {
//请求成功回调
@Override
public void onBannerAdLoadSuccess() {
}
//请求失败回调
@Override
public void onAdLoadFailed (int errorCode, String errorMsg) {
}
});
////废弃 将会在后面版本删除
// @Deprecated
// bannerAd.loadAd(upId, new BannerAd.BannerLoadListener() {
// //请求成功回调
// @Override
// public void onBannerAdLoadSuccess() {
// }
// //请求失败回调
// @Override
// public void onAdLoadFailed (int errorCode, String errorMsg) {
// }
// });
bannerAd.setDownLoadListener(new BannerAd.BannerDownloadListener() {
@Override
public void onDownloadStarted() {
//开始下载
LogUtils.d(TAG, "ads 开始下载");
}
@Override
public void onDownloadProgressUpdated(int progress) {
//下载进度,例如:${progress}%
LogUtils.d(TAG, String.format("ads 下载进度 %d", progress));
}
@Override
public void onDownloadPaused() {
//下载暂停
LogUtils.d(TAG, "ads 下载暂停");
}
@Override
public void onDownloadCancel() {
//取消下载
LogUtils.d(TAG, "ads 取消下载");
}
@Override
public void onDownloadFailed(int errorCode) {
//下载失败, 若需要了解errorCode具体含义请咨询米盟
LogUtils.d(TAG, String.format("ads 下载失败。errorCode %d", errorCode));
}
@Override
public void onDownloadFinished() {
//下载结束
LogUtils.d(TAG, "ads 下载结束");
}
@Override
public void onInstallStart() {
//开始安装
LogUtils.d(TAG, "ads 开始安装");
}
@Override
public void onInstallFailed(int errorCode) {
//安装失败
LogUtils.d(TAG, "ads 安装失败");
}
@Override
public void onInstallSuccess() {
//安装成功
LogUtils.d(TAG, "ads 安装成功");
}
});
LinearLayout ads_container = findViewById(R.id.ads_container);
bannerAd.showAd(this, ads_container, new BannerAd.BannerInteractionListener() {
@Override
public void onAdClick() {
// 广告被点击
}
@Override
public void onAdShow() {
// 广告被展示
}
@Override
public void onAdDismiss() {
// 点击关闭按钮广告消失回调
}
@Override
public void onRenderSuccess() {
// 广告渲染成功
}
@Override
public void onRenderFail(int code, String msg) {
// 广告渲染失败
}
});
}*/
@Override
protected void onDestroy() {
super.onDestroy();
//bannerAd.destroy();
}
void showFragment(Fragment fragment) {
FragmentTransaction tx = getFragmentManager().beginTransaction();
for (Fragment item : getFragmentManager().getFragments()) {
@@ -133,6 +311,24 @@ public class MainActivity extends WinBoLLActivity {
//NotificationHelper.cancelRemindNotification(this);
reloadBackground();
setBackgroundColor();
//LogUtils.d(TAG, "ads start");
new Thread(new Runnable(){
@Override
public void run() {
//LogUtils.d(TAG, "run() 1");
runOnUiThread(new Runnable(){
@Override
public void run() {
//LogUtils.d(TAG, "run() 2");
//try {
//Thread.sleep(1000);
fetchAd();
//} catch (InterruptedException e) {}
}
});
}
}).start();
}
// Menu icons are inflated just as they were with actionbar
@@ -159,7 +355,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);
@@ -172,7 +368,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 +413,275 @@ public class MainActivity extends WinBoLLActivity {
RelativeLayout mainLayout = findViewById(R.id.activitymainRelativeLayout1);
mainLayout.setBackgroundColor(nPixelColor);
}
private void showAd() {
LogUtils.d(TAG, "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() {
LogUtils.d(TAG, "onAdClick");
}
@Override
public void onAdShow() {
//mShowBtn.setEnabled(false);
LogUtils.d(TAG, "onAdShow");
}
@Override
public void onAdDismiss() {
LogUtils.d(TAG, "onAdDismiss");
mContainer.removeView(container);
}
@Override
public void onRenderSuccess() {
LogUtils.d(TAG, "onRenderSuccess");
}
@Override
public void onRenderFail(int code, String msg) {
LogUtils.e(TAG, "onRenderFail errorCode " + code + " errorMsg " + msg);
}
});
}
private void fetchAd() {
LogUtils.d(TAG, "fetchAd()");
mBannerAd = new BannerAd();
mAllBanners.add(mBannerAd);
mBannerAd.setDownLoadListener(new BannerAd.BannerDownloadListener() {
@Override
public void onDownloadStarted() {
LogUtils.d(TAG, "onDownloadStarted");
}
@Override
public void onDownloadPaused() {
LogUtils.d(TAG, "onDownloadPaused");
}
@Override
public void onDownloadFailed(int errorCode) {
LogUtils.d(TAG, "onDownloadFailed, errorCode = " + errorCode);
}
@Override
public void onDownloadFinished() {
LogUtils.d(TAG, "onDownloadFinished");
}
@Override
public void onDownloadProgressUpdated(int progress) {
LogUtils.d(TAG, "onDownloadProgressUpdated " + progress + "%");
}
@Override
public void onInstallFailed(int errorCode) {
LogUtils.d(TAG, "onInstallFailed, errorCode = " + errorCode);
}
@Override
public void onInstallStart() {
LogUtils.d(TAG, "onInstallStart");
}
@Override
public void onInstallSuccess() {
LogUtils.d(TAG, "onInstallSuccess");
}
@Override
public void onDownloadCancel() {
LogUtils.d(TAG, "onDownloadCancel");
}
});
String currentAD_ID = isEvenSecond()?BANNER_POS_ID_WINBOLL: BANNER_POS_ID;
LogUtils.d(TAG, String.format("currentAD_ID = %s", currentAD_ID));
ADParams params = new ADParams.Builder().setUpId(currentAD_ID).build();
mBannerAd.loadAd(params, new BannerAd.BannerLoadListener() {
@Override
public void onBannerAdLoadSuccess() {
LogUtils.d(TAG, "onBannerAdLoadSuccess()");
showAd();
//mShowBtn.setEnabled(true);
/*long bannerPrice = getPrice();
if (mIsBiddingWin) {
//竞价成功时候上报win
Map<String, Long> 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<String, Object> 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) {
LogUtils.d(TAG, "errorCode = " + errorCode + ", errorMsg = " + errorMsg);
}
});
}
/**
* 判断当前时间是单数秒还是双数秒
* @return true双数秒0、2、4...false单数秒1、3、5...
*/
private boolean isEvenSecond() {
// 1. 获取当前毫秒级时间戳
long currentTimeMillis = System.currentTimeMillis();
// 2. 转换为秒数(毫秒 ÷ 1000向下取整
long currentSecond = currentTimeMillis / 1000;
// 3. 对 2 取余:余数为 0 是双数秒,余数为 1 是单数秒
return currentSecond % 2 == 0;
}
/**
* 扩展:获取当前秒数的奇偶描述(直接返回文字说明)
* @return 字符串:"单数秒" 或 "双数秒"
*/
// private String getSecondParityDesc() {
// long currentSecond = System.currentTimeMillis() / 1000;
// return (currentSecond % 2 == 0) ? "双数秒" : "单数秒";
// }
private long getPrice() {
if (mBannerAd == null) {
return 0;
}
Map<String, Object> 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;
}
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -36,12 +36,9 @@ 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;
// 数据列表:仅存储包名、耗电、时长(不存储名称,计算全程用包名)
private List<AppBatteryModel> dataList = new ArrayList<AppBatteryModel>();
private List<AppBatteryModel> filteredList = new ArrayList<AppBatteryModel>();
private BroadcastReceiver batteryReceiver;
@@ -49,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<String, Long> appRunTimeCache = new HashMap<String, Long>();
// 新增:包名-应用名称映射缓存(用于搜索匹配名称,避免重复查询)
private Map<String, String> packageToAppNameCache = new HashMap<String, String>();
// 包管理工具(全局持有,避免重复获取)
private PackageManager mPackageManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_batteryreport);
// 初始化包管理工具(仅一次,供后续查询名称使用)
setContentView(R.layout.activity_battery_report);
mPackageManager = getPackageManager();
// 权限检查Java7 传统条件判断)
@@ -74,36 +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();
// 3. 强制添加目标应用(包名兜底)
//forceAddTargetPackage();
// 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) {
@@ -112,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;
@@ -142,92 +126,63 @@ public class BatteryReportActivity extends Activity {
}
/**
* 加载所有应用(仅获取包名,不查询应用名称,数据计算核心步骤
* 加载所有应用(仅获取包名,初始化模型时单次耗电、累计耗电均设为0
*/
private void loadAllAppPackage() {
List<ApplicationInfo> 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() + "个包名。" );
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 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);
}
/**
* 更新运行时长到模型(用包名匹配,不涉及名称)
* 更新运行时长到模型(逻辑不变
*/
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));
@@ -242,45 +197,94 @@ public class BatteryReportActivity extends Activity {
}
/**
* 新增:双维度过滤(包名+应用名称
* 搜索关键词同时匹配“包名(不区分大小写)”或“应用名称(不区分大小写)”
* 新增】初始化时计算24小时累计耗电赋值给totalConsumption
* 逻辑基于24小时运行时长占比分配当前电池容量的理论24小时消耗
*/
private void calculateInitial24hTotalConsumption() {
long total24hRunTime = 0;
// 1. 计算24小时内所有应用总运行时长
for (Map.Entry<String, Long> entry : appRunTimeCache.entrySet()) {
total24hRunTime += entry.getValue();
}
LogUtils.d(TAG, "24小时内所有应用总运行时长" + formatRunTime(total24hRunTime));
// 2. 按运行时长占比分配24小时累计耗电假设电池满电循环用总容量近似24小时总消耗
for (AppBatteryModel model : dataList) {
String packageName = model.getPackageName();
Long app24hRunTime = appRunTimeCache.getOrDefault(packageName, 0L);
// 计算占比与累计耗电
float ratio = (total24hRunTime > 0) ? (float) app24hRunTime / total24hRunTime : 0;
float initialTotalConsumption = batteryCapacity * ratio; // 用电池容量近似24小时总消耗
model.setTotalConsumption(initialTotalConsumption); // 初始化累计耗电
LogUtils.d(TAG, String.format("应用包 %s 24小时累计耗电初始化%.1f mAh", packageName, initialTotalConsumption));
}
}
/**
* 【核心修改】计算单次耗电赋值给consumption+ 累加至累计耗电totalConsumption = totalConsumption + consumption
*/
private void calculateSingleConsumptionAndAccumulate(float dropPercent, Map<String, Long> runTimeMap) {
long totalSingleRunTime = 0;
// 1. 计算本次电池下降期间的总运行时长
for (Map.Entry<String, Long> entry : runTimeMap.entrySet()) {
totalSingleRunTime += entry.getValue();
}
// 2. 遍历计算每个应用的“单次耗电”并“累加至累计”
for (AppBatteryModel model : dataList) {
String packageName = model.getPackageName();
Long appSingleRunTime = runTimeMap.getOrDefault(packageName, 0L);
// 步骤1计算本次单次耗电赋值给consumption
float ratio = (totalSingleRunTime > 0) ? (float) appSingleRunTime / totalSingleRunTime : 0;
float singleConsumption = batteryCapacity * dropPercent / 100 * ratio; // 单次消耗
model.setConsumption(singleConsumption); // 存储单次耗电
// 步骤2累加单次耗电到累计耗电totalConsumption = 原有累计 + 本次单次)
float newTotalConsumption = model.getTotalConsumption() + singleConsumption;
model.setTotalConsumption(newTotalConsumption); // 更新累计耗电
// 同步运行时长
model.setRunTime(appSingleRunTime);
LogUtils.d(TAG, String.format("应用包 %s单次耗电%.1f mAh累计耗电%.1f mAh",
packageName, singleConsumption, newTotalConsumption));
}
// 3. 按累计耗电排序(从高到低)
Collections.sort(dataList, new Comparator<AppBatteryModel>() {
@Override
public int compare(AppBatteryModel m1, AppBatteryModel m2) {
return Float.compare(m2.getTotalConsumption(), m1.getTotalConsumption());
}
});
// 4. 重新应用过滤并刷新列表
filterAppsByPackageAndName(etSearch.getText().toString());
}
/**
* 双维度过滤(逻辑不变)
*/
private void filterAppsByPackageAndName(String keyword) {
filteredList.clear();
if (keyword == null || keyword.isEmpty()) {
filteredList.addAll(dataList);
} else {
String lowerKeyword = keyword.toLowerCase(); // 统一转为小写,实现不区分大小写搜索
boolean isTargetMatched = false;
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);
// 检查是否是目标包名
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;
}
}
}
}
@@ -288,7 +292,7 @@ public class BatteryReportActivity extends Activity {
}
/**
* 获取应用运行时长(返回包名-时长映射,不处理名称
* 获取应用运行时长(逻辑不变返回24小时运行时长
*/
private Map<String, Long> getAppRunTime() {
Map<String, Long> runTimeMap = new HashMap<String, Long>();
@@ -301,11 +305,10 @@ public class BatteryReportActivity extends Activity {
List<android.app.usage.UsageStats> statsList = manager.queryUsageStats(
android.app.usage.UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
// 仅存储包名和时长(不查名称)
for (android.app.usage.UsageStats stats : statsList) {
long runTimeMs = stats.getTotalTimeInForeground();
String packageName = stats.getPackageName();
LogUtils.d(TAG, "包名" + packageName + "运行时长:" + 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));
@@ -321,7 +324,7 @@ public class BatteryReportActivity extends Activity {
}
/**
* 格式化运行时长(工具方法,与包名无关
* 格式化运行时长(逻辑不变
*/
private String formatRunTime(long runTimeMs) {
if (runTimeMs <= 0) {
@@ -342,48 +345,7 @@ public class BatteryReportActivity extends Activity {
}
/**
* 分配电池消耗(全程用包名计算,不涉及名称
*/
private void distributeBatteryConsumption(float totalDropPercent, Map<String, Long> runTimeMap) {
long totalRunTime = 0;
// 遍历包名计算总时长
for (Map.Entry<String, Long> 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<AppBatteryModel>() {
@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) {
@@ -405,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; // 单次耗电mAhfloat类型
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() {
@@ -442,37 +416,37 @@ public class BatteryReportActivity extends Activity {
}
/**
* RecyclerView 适配器(支持从缓存获取名称,避免重复查询)
* RecyclerView 适配器仅显示累计耗电totalConsumption逻辑适配模型修改
*/
public static class BatteryReportAdapter extends RecyclerView.Adapter<BatteryReportAdapter.ViewHolder> {
private Context mContext;
private List<AppBatteryModel> mDataList;
private PackageManager mPm; // 兜底用:缓存无名称时查询
private Map<String, String> mPackageToNameCache; // 新增:名称缓存映射
private PackageManager mPm;
private Map<String, String> mPackageToNameCache;
// Java7 显式构造(新增名称缓存参数)
// Java7 显式构造:接收名称缓存,确保显示时高效获取应用名
public BatteryReportAdapter(Context context, List<AppBatteryModel> dataList,
PackageManager pm, Map<String, String> packageToNameCache) {
this.mContext = context;
this.mDataList = dataList;
this.mPm = pm;
this.mPackageToNameCache = packageToNameCache; // 接收名称缓存
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;
}
@@ -480,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);
// 优化显示:目标应用文字标蓝(通过包名匹配
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.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系统布局固定IDtext1、text2
public ViewHolder(View itemView) {
super(itemView);
// 强制类型转换(系统布局控件 IDtext1 和 text2
tvAppName = (TextView) itemView.findViewById(android.R.id.text1);
tvConsumption = (TextView) itemView.findViewById(android.R.id.text2);
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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() {
Toolbar 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);
}
}

View File

@@ -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);

View File

@@ -0,0 +1,33 @@
package cc.winboll.studio.powerbell.utils;
import android.content.Context;
import android.util.DisplayMetrics;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @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);
}
}

View File

@@ -6,7 +6,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
<cc.winboll.studio.libaes.views.AToolbar
<cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height"
android:id="@+id/toolbar"
@@ -22,14 +22,28 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/activitymainRelativeLayout1"
android:background="#FFEE2121"/>
android:background="#FFB7B7B7"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/activitymainFrameLayout1"/>
</RelativeLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/activitymainLinearLayout1">
<LinearLayout
android:id="@+id/ads_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
</LinearLayout>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_battery_reporter"
android:title="@string/item_battery_reporter"/>
android:id="@+id/action_battery_report"
android:title="@string/item_battery_report"/>
<item
android:id="@+id/action_clearrecord"
android:title="@string/item_clearrecord"/>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">能源钟</string>
<string name="app_name">PowerBell</string>
<string name="app_description">一个接收手机电量信息的应用,当电量值达到设定范围时会提醒用户。</string>
<string name="about_crashed">本应用崩溃了,作者水平有限,敬请谅解!</string>
<string name="item_mainview">Main View</string>

View File

@@ -25,16 +25,15 @@
<color name="colorShuiDark">#FF0072A4</color>
<color name="colorShuiAccent">#FF33C1FF</color>
<color name="colorXinling">#FFFCC500</color>
<color name="colorPrimary">@color/colorShuiDark</color>
<!--<color name="colorPrimary">@color/colorShuiDark</color>
<color name="colorPrimaryDark">@color/colorFeng</color>
<color name="colorAccent">@color/colorShui</color>
<color name="colorYellow">#FFFFFF00</color>
<color name="colorRed">#FFFF0000</color>
<color name="colorBlue">#FF000FFF</color>
-->
<color name="colorYellow">#FFBEBE48</color>
<color name="colorRed">#FFC85C5C</color>
<color name="colorBlue">#FF2677C7</color>
<color name="colorBlack">#FF000000</color>
<color name="colorUsege">@color/colorHuo</color>
<color name="colorCurrent">@color/colorShui</color>
<color name="colorCharge">@color/colorXinling</color>
<color name="colorText">@color/colorBlack</color>
<!-- 调试配置
<color name="colorYellow">#FF630066</color>
<color name="colorRed">#FF23244D</color>
@@ -47,15 +46,19 @@
<color name="colorBlue">#FF000DE5</color>
<color name="colorBlack">#FF000000</color>
-->
<!-- 原色配置
<color name="colorYellow">#FFFFFF00</color>
<!-- 原色配置 -->
<!--<color name="colorYellow">#FFFFFF00</color>
<color name="colorRed">#FFFF0000</color>
<color name="colorBlue">#FF000FFF</color>
<color name="colorBlack">#FF000000</color>
-->
<color name="colorPrimary">@color/colorShui</color>
<color name="colorPrimaryDark">@color/colorShuiDark</color>
<color name="colorAccent">@color/colorShuiAccent</color>
<color name="colorUsege">@color/colorRed</color>
<color name="colorCurrent">@color/colorBlue</color>
<color name="colorCharge">@color/colorYellow</color>
-->
<!--CustomSlideToUnlockView控件配置-->
<color name="colorCustomSlideToUnlockViewWhite">#FFFFFFFF</color>

View File

@@ -6,7 +6,7 @@
<string name="about_crashed">This application has crashed, the author level is limited, please understand!</string>
<string name="item_mainview">Main View</string>
<string name="item_aboutview">About</string>
<string name="item_battery_reporter">Battery Reporter</string>
<string name="item_battery_report">Battery Report</string>
<string name="item_clearrecord">Clear Record</string>
<string name="item_changepicture">Change Picture</string>
<string name="item_devoloperoptionsview">Developer View</string>

View File

@@ -13,6 +13,9 @@
<external-files-path
name="external_file_path"
path="." />
<external-files-path
name="files_root"
path="mimoDownload" />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
<external-cache-path
name="external_cache_path"

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
<debug-overrides>
<trust-anchors>
<!-- Trust user added CAs while debuggable only -->
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>