Compare commits
30 Commits
powerbell-
...
powerbell-
| Author | SHA1 | Date | |
|---|---|---|---|
| e51d46186a | |||
| 8fc6855066 | |||
| 4ceaf1e46a | |||
| e669bbb04b | |||
| 6bf3ebe2fd | |||
| a44f7fe6d4 | |||
| 35a34b5b53 | |||
| d35d0d0291 | |||
| 03212c0554 | |||
| 0c963213df | |||
| 10ddca4f73 | |||
| f240d9c057 | |||
| 2c77bf775b | |||
| 1db7c9bf80 | |||
| fd556fd06f | |||
| 220aa9dbfb | |||
| ecafd2026f | |||
| 6ed9bc0d8e | |||
| bcb5db0a17 | |||
| 6b69e04706 | |||
| a2a61bbf0b | |||
| 9f4211c83e | |||
| 447a786632 | |||
| ff0f239ffc | |||
| 7c59a982fc | |||
| 895cc4630d | |||
| 851a539364 | |||
| d79f2937ba | |||
| c524a21429 | |||
| a148de2ab8 |
@@ -29,11 +29,11 @@ android {
|
||||
applicationId "cc.winboll.studio.powerbell"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 30
|
||||
versionCode 6
|
||||
versionCode 7
|
||||
// versionName 更新后需要手动设置
|
||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "15.12"
|
||||
versionName "15.14"
|
||||
if(true) {
|
||||
versionName = genVersionName("${versionName}")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Dec 10 18:32:41 HKT 2025
|
||||
stageCount=9
|
||||
#Sat Dec 13 13:05:44 GMT 2025
|
||||
stageCount=0
|
||||
libraryProject=
|
||||
baseVersion=15.12
|
||||
publishVersion=15.12.8
|
||||
buildCount=0
|
||||
baseBetaVersion=15.12.9
|
||||
baseVersion=15.14
|
||||
publishVersion=15.14.0
|
||||
buildCount=1
|
||||
baseBetaVersion=15.14.1
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="cc.winboll.studio.powerbell">
|
||||
|
||||
<!-- 只能在前台获取精确的位置信息 -->
|
||||
<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"/>
|
||||
|
||||
@@ -143,14 +137,14 @@
|
||||
</activity-alias>
|
||||
|
||||
<activity
|
||||
android:name="cc.winboll.studio.powerbell.activities.ClearRecordActivity"
|
||||
android:name=".activities.ClearRecordActivity"
|
||||
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
|
||||
android:launchMode="singleTask">
|
||||
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="cc.winboll.studio.powerbell.activities.BackgroundSettingsActivity"
|
||||
android:name=".activities.BackgroundSettingsActivity"
|
||||
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask">
|
||||
@@ -190,14 +184,14 @@
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="cc.winboll.studio.powerbell.services.ControlCenterService"
|
||||
android:name=".services.ControlCenterService"
|
||||
android:priority="1000"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:process=".controlcenterservice"/>
|
||||
|
||||
<service
|
||||
android:name="cc.winboll.studio.powerbell.services.AssistantService"
|
||||
android:name=".services.AssistantService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:process=".assistantservice"/>
|
||||
@@ -206,13 +200,13 @@
|
||||
android:name="android.max_aspect"
|
||||
android:value="4.0"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.BatteryReporterActivity"/>
|
||||
<activity android:name=".activities.BatteryReporterActivity"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.PixelPickerActivity"/>
|
||||
<activity android:name=".activities.PixelPickerActivity"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.BatteryReportActivity"/>
|
||||
<activity android:name=".activities.BatteryReportActivity"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.unittest.MainUnitTestActivity"/>
|
||||
<activity android:name=".unittest.MainUnitTestActivity"/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
@@ -226,16 +220,16 @@
|
||||
|
||||
</provider>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"/>
|
||||
<activity android:name=".activities.ShortcutActionActivity"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.powerbell.activities.SettingsActivity"/>
|
||||
<activity android:name=".activities.SettingsActivity"/>
|
||||
|
||||
<!-- 1. 注册 UCropActivity(关键:解决崩溃) -->
|
||||
<activity
|
||||
android:name="com.yalantis.ucrop.UCropActivity"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
||||
android:exported="true"> <!-- 必须添加:Android 12+ 要求显式声明 exported -->
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.yalantis.ucrop.UCropActivity"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
||||
android:exported="true">
|
||||
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
BIN
powerbell/src/main/assets/images/blank10x10.png
Normal file
BIN
powerbell/src/main/assets/images/blank10x10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 B |
@@ -2,28 +2,37 @@ package cc.winboll.studio.powerbell;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.models.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver;
|
||||
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BitmapCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.PermissionUtils;
|
||||
import java.io.File;
|
||||
|
||||
public class App extends GlobalApplication {
|
||||
|
||||
public static final String TAG = "App";
|
||||
|
||||
public static final String COMPONENT_EN1 = "cc.winboll.studio.powerbell.MainActivityEN1";
|
||||
public static final String COMPONENT_CN1 = "cc.winboll.studio.powerbell.MainActivityCN1";
|
||||
public static final String COMPONENT_CN2 = "cc.winboll.studio.powerbell.MainActivityCN2";
|
||||
public static final String ACTION_SWITCHTO_EN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1";
|
||||
public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1";
|
||||
public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2";
|
||||
public static final String COMPONENT_EN1 = "cc.winboll.studio.powerbell.MainActivityEN1";
|
||||
public static final String COMPONENT_CN1 = "cc.winboll.studio.powerbell.MainActivityCN1";
|
||||
public static final String COMPONENT_CN2 = "cc.winboll.studio.powerbell.MainActivityCN2";
|
||||
public static final String ACTION_SWITCHTO_EN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1";
|
||||
public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1";
|
||||
public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2";
|
||||
|
||||
// 数据配置存储工具
|
||||
static AppConfigUtils _mAppConfigUtils;
|
||||
static AppCacheUtils _mAppCacheUtils;
|
||||
// 新增:全局 Bitmap 缓存工具(常驻内存)
|
||||
public static BitmapCacheUtils _mBitmapCacheUtils;
|
||||
|
||||
GlobalApplicationReceiver mReceiver;
|
||||
static String szTempDir = "";
|
||||
|
||||
@@ -34,25 +43,16 @@ public class App extends GlobalApplication {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
//setIsDebugging(false);
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
|
||||
// 初始化活动窗口管理
|
||||
WinBoLLActivityManager.init(this);
|
||||
// 初始化活动窗口管理
|
||||
WinBoLLActivityManager.init(this);
|
||||
// 初始化 Toast 框架
|
||||
ToastUtils.init(this);
|
||||
|
||||
// 临时文件夹方案1
|
||||
// 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
|
||||
|
||||
// 临时文件夹初始化(保持原有逻辑)
|
||||
File picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
// 定义目标文件路径(在Pictures目录下创建"PowerBell"子文件夹及文件)
|
||||
File powerBellDir = new File(picturesDir, "PowerBell");
|
||||
|
||||
// 临时文件夹方案2 <图片保存失败>
|
||||
// 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
|
||||
//File powerBellDir = getExternalFilesDir("TempDir");
|
||||
|
||||
// 先创建文件夹(如果不存在)
|
||||
if (!powerBellDir.exists()) {
|
||||
powerBellDir.mkdirs();
|
||||
}
|
||||
@@ -61,11 +61,54 @@ public class App extends GlobalApplication {
|
||||
// 设置数据配置存储工具
|
||||
_mAppConfigUtils = getAppConfigUtils(this);
|
||||
_mAppCacheUtils = getAppCacheUtils(this);
|
||||
// 初始化全局 Bitmap 缓存工具(关键:App 启动时初始化,常驻内存)
|
||||
_mBitmapCacheUtils = BitmapCacheUtils.getInstance();
|
||||
|
||||
mReceiver = new GlobalApplicationReceiver(this);
|
||||
mReceiver.registerAction();
|
||||
|
||||
// ======================== 新增:异步预加载背景图 ========================
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// 1. 获取背景源工具类实例
|
||||
BackgroundSourceUtils bgSourceUtils = BackgroundSourceUtils.getInstance(App.this);
|
||||
if (bgSourceUtils == null) {
|
||||
LogUtils.e(TAG, "preloadBitmap: BackgroundSourceUtils 实例为空");
|
||||
return;
|
||||
}
|
||||
// 2. 获取当前背景Bean
|
||||
BackgroundBean bgBean = bgSourceUtils.getCurrentBackgroundBean();
|
||||
if (bgBean == null || !bgBean.isUseBackgroundFile()) {
|
||||
LogUtils.d(TAG, "preloadBitmap: 无有效背景文件,跳过预加载");
|
||||
return;
|
||||
}
|
||||
// 3. 获取背景图路径(优先取压缩图路径)
|
||||
String bgPath = bgBean.isUseBackgroundScaledCompressFile()
|
||||
? bgBean.getBackgroundScaledCompressFilePath()
|
||||
: bgBean.getBackgroundFilePath();
|
||||
// 4. 预加载到全局缓存
|
||||
if (_mBitmapCacheUtils != null) {
|
||||
_mBitmapCacheUtils.cacheBitmap(bgPath);
|
||||
LogUtils.d(TAG, "preloadBitmap: 应用启动时预加载成功 - " + bgPath);
|
||||
} else {
|
||||
LogUtils.e(TAG, "preloadBitmap: 全局 BitmapCacheUtils 未初始化");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "preloadBitmap: 预加载失败 - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}, 1000); // 延迟1秒执行,避免阻塞应用初始化
|
||||
// ======================== 预加载逻辑结束 ========================
|
||||
}
|
||||
|
||||
// 保持原有方法不变
|
||||
public static AppConfigUtils getAppConfigUtils(Context context) {
|
||||
if (_mAppConfigUtils == null) {
|
||||
_mAppConfigUtils = AppConfigUtils.getInstance(context);
|
||||
@@ -84,12 +127,14 @@ public class App extends GlobalApplication {
|
||||
_mAppCacheUtils.clearBatteryHistory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
ToastUtils.release();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
ToastUtils.release();
|
||||
// 可选:App 终止时清空 Bitmap 缓存,释放内存
|
||||
if (_mBitmapCacheUtils != null) {
|
||||
_mBitmapCacheUtils.clearAllCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package cc.winboll.studio.powerbell;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -15,6 +14,7 @@ import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
@@ -37,7 +37,7 @@ import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
|
||||
import cc.winboll.studio.powerbell.activities.SettingsActivity;
|
||||
import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
|
||||
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.models.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import cc.winboll.studio.powerbell.unittest.MainUnitTestActivity;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
@@ -49,71 +49,56 @@ import cc.winboll.studio.powerbell.views.BatteryDrawable;
|
||||
import cc.winboll.studio.powerbell.views.VerticalSeekBar;
|
||||
|
||||
/**
|
||||
* 主活动类(修复小米广告SDK空Context崩溃问题)
|
||||
* 核心修改点:
|
||||
* 1. 添加全局Context安全校验
|
||||
* 2. 优化广告请求的生命周期判断
|
||||
* 3. 确保广告操作在主线程执行
|
||||
* 4. 完善广告资源释放逻辑
|
||||
* 主活动类(Java 7 兼容 | 移除窗体缓存)
|
||||
* 核心优化点:
|
||||
* 1. ViewHolder 缓存控件 + ViewStub 延迟加载
|
||||
* 2. 资源异步加载 + 非关键任务异步化
|
||||
*/
|
||||
public class MainActivity extends WinBoLLActivity {
|
||||
|
||||
// ======================== 静态常量(首屏排列,统一管理)========================
|
||||
// ======================== 静态常量(移除缓存相关键)========================
|
||||
public static final String TAG = "MainActivity";
|
||||
private static final int REQUEST_WRITE_STORAGE_PERMISSION = 1001;
|
||||
|
||||
// ======================== 成员属性(按「静态→非静态」「核心→辅助」排序)========================
|
||||
public static MainActivity _mMainActivity; // 静态Activity实例(全局调用)
|
||||
private App mApplication; // 应用Application实例
|
||||
private Menu mMenu; // 顶部菜单实例
|
||||
private Fragment mCurrentShowFragment; // 当前显示的Fragment
|
||||
private MainViewFragment mMainViewFragment; // 主Fragment(核心视图)
|
||||
private Toolbar mToolbar; // 顶部工具栏
|
||||
private ADsBannerView mADsBannerView; // 广告Banner视图
|
||||
|
||||
public static final int MSG_RELOAD_APPCONFIG = 0;
|
||||
private static final int REQUEST_READ_MEDIA_IMAGES = 1001;
|
||||
public static final int MSG_RELOAD_APPCONFIG = 0;
|
||||
public static final int MSG_CURRENTVALUEBATTERY = 1;
|
||||
public static final int MSG_LOAD_BACKGROUND = 2;
|
||||
private static final int DELAY_LOAD_NON_CRITICAL = 500;
|
||||
|
||||
// ======================== 成员属性(移除缓存标识)========================
|
||||
public static MainActivity _mMainActivity;
|
||||
static MainViewFragment _mMainViewFragment;
|
||||
AppConfigUtils mAppConfigUtils;
|
||||
Drawable mDrawableFrame;
|
||||
LinearLayout mllLeftSeekBar;
|
||||
LinearLayout mllRightSeekBar;
|
||||
CheckBox mcbIsEnableChargeReminder;
|
||||
CheckBox mcbIsEnableUsegeReminder;
|
||||
Switch mswIsEnableService;
|
||||
TextView mtvTips;
|
||||
static Handler _mHandler;
|
||||
|
||||
private BackgroundSourceUtils mBgSourceUtils;
|
||||
private App mApplication;
|
||||
private AppConfigUtils mAppConfigUtils;
|
||||
private BackgroundSourceUtils mBgSourceUtils;
|
||||
|
||||
// 背景布局
|
||||
//LinearLayout mLinearLayoutloadBackground;
|
||||
private Toolbar mToolbar;
|
||||
private ViewStub mAdsViewStub;
|
||||
private ADsBannerView mADsBannerView;
|
||||
|
||||
// 现在电量图示
|
||||
BatteryDrawable mCurrentValueBatteryDrawable;
|
||||
// 现在充电提醒电量图示
|
||||
BatteryDrawable mChargeReminderValueBatteryDrawable;
|
||||
// 现在耗电提醒电量图示
|
||||
BatteryDrawable mUsegeReminderValueBatteryDrawable;
|
||||
private ViewHolder mViewHolder;
|
||||
private Menu mMenu;
|
||||
private Fragment mCurrentShowFragment;
|
||||
private MainViewFragment mMainViewFragment;
|
||||
private Drawable mDrawableFrame;
|
||||
private BatteryDrawable mCurrentValueBatteryDrawable;
|
||||
private BatteryDrawable mChargeReminderValueBatteryDrawable;
|
||||
private BatteryDrawable mUsegeReminderValueBatteryDrawable;
|
||||
|
||||
ImageView mCurrentValueBatteryImageView;
|
||||
ImageView mChargeReminderValueBatteryImageView;
|
||||
ImageView mUsegeReminderValueBatteryImageView;
|
||||
// ======================== 视图缓存容器(ViewHolder 不变)========================
|
||||
private static class ViewHolder {
|
||||
BackgroundView backgroundView;
|
||||
RelativeLayout mainLayout;
|
||||
LinearLayout llLeftSeekBar, llRightSeekBar;
|
||||
CheckBox cbIsEnableChargeReminder, cbIsEnableUsegeReminder;
|
||||
Switch swIsEnableService;
|
||||
TextView tvTips, tvChargeReminderValue, tvUsegeReminderValue, tvCurrentValue;
|
||||
VerticalSeekBar chargeReminderSeekBar, usegeReminderSeekBar;
|
||||
ImageView ivCurrentBattery, ivChargeReminderBattery, ivUsegeReminderBattery;
|
||||
}
|
||||
|
||||
VerticalSeekBar mChargeReminderSeekBar;
|
||||
ChargeReminderSeekBarChangeListener mChargeReminderSeekBarChangeListener;
|
||||
TextView mtvChargeReminderValue;
|
||||
|
||||
|
||||
VerticalSeekBar mUsegeReminderSeekBar;
|
||||
UsegeReminderSeekBarChangeListener mUsegeReminderSeekBarChangeListener;
|
||||
TextView mtvUsegeReminderValue;
|
||||
CheckBox mcbUsegeReminderValue;
|
||||
TextView mtvCurrentValue;
|
||||
BackgroundView mBackgroundView;
|
||||
|
||||
|
||||
// ======================== 重写父类抽象方法(优先排列,明确实现)========================
|
||||
// ======================== 重写生命周期(移除缓存恢复/保存逻辑)========================
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
@@ -126,51 +111,58 @@ 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);
|
||||
initGlobalHandler();
|
||||
|
||||
// 初始化视图与实例
|
||||
initView();
|
||||
initInstance();
|
||||
// initFragment();
|
||||
// 恢复原始初始化流程:直接加载布局+初始化
|
||||
setContentView(R.layout.activity_main);
|
||||
initCoreUtilsAsync();
|
||||
initViewHolder();
|
||||
initCriticalView();
|
||||
loadNonCriticalViewDelayed();
|
||||
|
||||
// 权限申请
|
||||
PermissionUtils.getInstance().checkAndRequestStoragePermission(this);
|
||||
}
|
||||
PermissionUtils.getInstance().checkAndRequestMediaImagesPermission(this, REQUEST_READ_MEDIA_IMAGES);
|
||||
}
|
||||
|
||||
// 移除 onSaveInstanceState 方法
|
||||
// 移除 onRestoreInstanceState 方法
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// 释放广告资源
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.releaseAdResources();
|
||||
}
|
||||
// 置空静态引用,避免内存泄漏
|
||||
_mMainActivity = null;
|
||||
_mMainViewFragment = null;
|
||||
if (_mHandler != null) {
|
||||
_mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
reloadBackground();
|
||||
setBackgroundColor();
|
||||
// 恢复广告
|
||||
// 移除缓存恢复后的刷新逻辑,直接发送加载背景消息
|
||||
if (_mHandler != null) {
|
||||
_mHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND);
|
||||
}
|
||||
if (mADsBannerView != null) {
|
||||
mADsBannerView.resumeADs(MainActivity.this);
|
||||
}
|
||||
}
|
||||
|
||||
// 其他生命周期方法(onCreateOptionsMenu、onOptionsItemSelected等保持原有不变)
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
mMenu = menu;
|
||||
// 主题菜单
|
||||
AESThemeUtil.inflateMenu(this, menu);
|
||||
// 调试工具菜单(仅Debug模式显示)
|
||||
if (App.isDebugging()) {
|
||||
DevelopUtils.inflateMenu(this, menu);
|
||||
getMenuInflater().inflate(R.menu.toolbar_unittest, mMenu);
|
||||
}
|
||||
// 应用核心菜单
|
||||
getMenuInflater().inflate(R.menu.toolbar_main, mMenu);
|
||||
return true;
|
||||
}
|
||||
@@ -178,17 +170,14 @@ public class MainActivity extends WinBoLLActivity {
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int menuItemId = item.getItemId();
|
||||
// 主题切换菜单
|
||||
if (AESThemeUtil.onAppThemeItemSelected(this, item)) {
|
||||
recreate();
|
||||
return true;
|
||||
}
|
||||
// 调试工具菜单
|
||||
if (DevelopUtils.onDevelopItemSelected(this, item)) {
|
||||
LogUtils.d(TAG, String.format("onOptionsItemSelected item.getItemId() %d ", item.getItemId()));
|
||||
return true;
|
||||
}
|
||||
// 应用核心菜单
|
||||
switch (menuItemId) {
|
||||
case R.id.action_settings:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
@@ -220,15 +209,24 @@ public class MainActivity extends WinBoLLActivity {
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (_mHandler != null) {
|
||||
_mHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == REQUEST_READ_MEDIA_IMAGES) {
|
||||
PermissionUtils.getInstance().handleStoragePermissionResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mCurrentShowFragment != mMainViewFragment) {
|
||||
showFragment(mMainViewFragment);
|
||||
} else {
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -244,50 +242,374 @@ public class MainActivity extends WinBoLLActivity {
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 移除缓存相关核心方法(restoreFromCache/refreshViewFromCache)========================
|
||||
|
||||
// ======================== 核心业务逻辑方法(按功能优先级排序)========================
|
||||
/**
|
||||
* 显示指定Fragment(隐藏其他Fragment)
|
||||
* 初始化电量图标(复用逻辑)
|
||||
*/
|
||||
void showFragment(Fragment fragment) {
|
||||
FragmentTransaction tx = getFragmentManager().beginTransaction();
|
||||
for (Fragment item : getFragmentManager().getFragments()) {
|
||||
tx.hide(item);
|
||||
private void initBatteryDrawables() {
|
||||
if (mCurrentValueBatteryDrawable == null) {
|
||||
mCurrentValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorCurrent));
|
||||
}
|
||||
if (mChargeReminderValueBatteryDrawable == null) {
|
||||
mChargeReminderValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorCharge));
|
||||
}
|
||||
if (mUsegeReminderValueBatteryDrawable == null) {
|
||||
mUsegeReminderValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorUsege));
|
||||
}
|
||||
tx.show(fragment);
|
||||
tx.commit();
|
||||
mCurrentShowFragment = fragment;
|
||||
}
|
||||
|
||||
// ======================== 原有核心方法(移除缓存判断逻辑)========================
|
||||
/**
|
||||
* 刷新背景(全局静态调用,带生命周期校验)
|
||||
* 刷新背景(移除缓存判断)
|
||||
*/
|
||||
// public static void reloadBackground() {
|
||||
// if (_mMainActivity != null && !_mMainActivity.isFinishing() && !_mMainActivity.isDestroyed()) {
|
||||
// _mMainActivity.mMainViewFragment.reloadBackground();
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 设置主页面背景颜色(从BackgroundBean获取颜色值)
|
||||
*/
|
||||
void setBackgroundColor() {
|
||||
// 生命周期校验,避免Activity销毁后操作UI
|
||||
if (isFinishing() || isDestroyed()) {
|
||||
public void reloadBackground() {
|
||||
if (mViewHolder == null || mBgSourceUtils == null || mViewHolder.backgroundView == null) {
|
||||
LogUtils.e(TAG, "reloadBackground: 背景加载失败(控件/工具类未初始化)");
|
||||
return;
|
||||
}
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||
int nPixelColor = bean.getPixelColor();
|
||||
RelativeLayout mainLayout = findViewById(R.id.activitymainRelativeLayout1);
|
||||
if (mainLayout != null) {
|
||||
mainLayout.setBackgroundColor(nPixelColor);
|
||||
BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean();
|
||||
if (bean != null) {
|
||||
mViewHolder.backgroundView.loadBackgroundBean(bean);
|
||||
} else {
|
||||
LogUtils.e(TAG, "reloadBackground: 背景Bean为空(图片路径异常)");
|
||||
mViewHolder.backgroundView.setBackgroundResource(R.drawable.default_background);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Uri获取文件本地真实路径(兼容图片选择等场景)
|
||||
* 设置主页面背景颜色(移除缓存判断)
|
||||
*/
|
||||
void setBackgroundColor() {
|
||||
if (isFinishing() || isDestroyed() || mViewHolder == null || mBgSourceUtils == null || mViewHolder.mainLayout == null) {
|
||||
return;
|
||||
}
|
||||
BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean();
|
||||
if (bean != null) {
|
||||
int nPixelColor = bean.getPixelColor();
|
||||
mViewHolder.mainLayout.setBackgroundColor(nPixelColor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视图数据(移除缓存判断)
|
||||
*/
|
||||
void setViewData() {
|
||||
if (mViewHolder == null || mAppConfigUtils == null) return;
|
||||
|
||||
int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue();
|
||||
int nUsegeReminderValue = mAppConfigUtils.getUsegeReminderValue();
|
||||
int nCurrentValue = mAppConfigUtils.getCurrentValue();
|
||||
|
||||
if (mViewHolder.llLeftSeekBar != null && mViewHolder.llRightSeekBar != null && mDrawableFrame != null) {
|
||||
mViewHolder.llLeftSeekBar.setBackground(mDrawableFrame);
|
||||
mViewHolder.llRightSeekBar.setBackground(mDrawableFrame);
|
||||
}
|
||||
|
||||
initBatteryDrawables();
|
||||
if (mViewHolder.ivCurrentBattery != null) {
|
||||
mCurrentValueBatteryDrawable.setValue(nCurrentValue);
|
||||
mViewHolder.ivCurrentBattery.setImageDrawable(mCurrentValueBatteryDrawable);
|
||||
}
|
||||
if (mViewHolder.ivChargeReminderBattery != null) {
|
||||
mChargeReminderValueBatteryDrawable.setValue(nChargeReminderValue);
|
||||
mViewHolder.ivChargeReminderBattery.setImageDrawable(mChargeReminderValueBatteryDrawable);
|
||||
}
|
||||
if (mViewHolder.ivUsegeReminderBattery != null) {
|
||||
mUsegeReminderValueBatteryDrawable.setValue(nUsegeReminderValue);
|
||||
mViewHolder.ivUsegeReminderBattery.setImageDrawable(mUsegeReminderValueBatteryDrawable);
|
||||
}
|
||||
|
||||
if (mViewHolder.tvChargeReminderValue != null) {
|
||||
mViewHolder.tvChargeReminderValue.setTextColor(getActivity().getColor(R.color.colorCharge));
|
||||
mViewHolder.tvChargeReminderValue.setText(String.valueOf(nChargeReminderValue) + "%");
|
||||
}
|
||||
if (mViewHolder.chargeReminderSeekBar != null) {
|
||||
mViewHolder.chargeReminderSeekBar.setProgress(nChargeReminderValue);
|
||||
}
|
||||
if (mViewHolder.cbIsEnableChargeReminder != null) {
|
||||
mViewHolder.cbIsEnableChargeReminder.setChecked(mAppConfigUtils.getIsEnableChargeReminder());
|
||||
}
|
||||
|
||||
if (mViewHolder.tvUsegeReminderValue != null) {
|
||||
mViewHolder.tvUsegeReminderValue.setTextColor(getActivity().getColor(R.color.colorUsege));
|
||||
mViewHolder.tvUsegeReminderValue.setText(String.valueOf(nUsegeReminderValue) + "%");
|
||||
}
|
||||
if (mViewHolder.usegeReminderSeekBar != null) {
|
||||
mViewHolder.usegeReminderSeekBar.setProgress(nUsegeReminderValue);
|
||||
}
|
||||
if (mViewHolder.cbIsEnableUsegeReminder != null) {
|
||||
mViewHolder.cbIsEnableUsegeReminder.setChecked(mAppConfigUtils.getIsEnableUsegeReminder());
|
||||
}
|
||||
|
||||
if (mViewHolder.tvCurrentValue != null) {
|
||||
mViewHolder.tvCurrentValue.setTextColor(getActivity().getColor(R.color.colorCurrent));
|
||||
mViewHolder.tvCurrentValue.setText(String.valueOf(nCurrentValue) + "%");
|
||||
}
|
||||
|
||||
if (mViewHolder.swIsEnableService != null) {
|
||||
mViewHolder.swIsEnableService.setChecked(mAppConfigUtils.getIsEnableService());
|
||||
mViewHolder.swIsEnableService.setText(getString(R.string.txt_aboveswitch));
|
||||
}
|
||||
if (mViewHolder.tvTips != null) {
|
||||
mViewHolder.tvTips.setText(getString(R.string.txt_aboveswitchtips));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视图监听(保持原有)
|
||||
*/
|
||||
void setViewListener() {
|
||||
if (mViewHolder == null || mAppConfigUtils == null) return;
|
||||
|
||||
if (mViewHolder.chargeReminderSeekBar != null) {
|
||||
mViewHolder.chargeReminderSeekBar.setOnSeekBarChangeListener(new ChargeReminderSeekBarChangeListener());
|
||||
}
|
||||
if (mViewHolder.cbIsEnableChargeReminder != null) {
|
||||
mViewHolder.cbIsEnableChargeReminder.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mAppConfigUtils.setIsEnableChargeReminder(mViewHolder.cbIsEnableChargeReminder.isChecked());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mViewHolder.usegeReminderSeekBar != null) {
|
||||
mViewHolder.usegeReminderSeekBar.setOnSeekBarChangeListener(new UsegeReminderSeekBarChangeListener());
|
||||
}
|
||||
if (mViewHolder.cbIsEnableUsegeReminder != null) {
|
||||
mViewHolder.cbIsEnableUsegeReminder.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mAppConfigUtils.setIsEnableUsegeReminder(mViewHolder.cbIsEnableUsegeReminder.isChecked());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mViewHolder.swIsEnableService != null) {
|
||||
mViewHolder.swIsEnableService.setOnClickListener(new CompoundButton.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mAppConfigUtils.setIsEnableService(getActivity(), mViewHolder.swIsEnableService.isChecked());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前电量显示(保持原有)
|
||||
*/
|
||||
void setCurrentValueBattery(int value) {
|
||||
if (mViewHolder == null || mCurrentValueBatteryDrawable == null || mViewHolder.tvCurrentValue == null) return;
|
||||
mViewHolder.tvCurrentValue.setText(String.valueOf(value) + "%");
|
||||
mCurrentValueBatteryDrawable.setValue(value);
|
||||
mCurrentValueBatteryDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化ViewHolder(保持原有)
|
||||
*/
|
||||
private void initViewHolder() {
|
||||
mViewHolder = new ViewHolder();
|
||||
mViewHolder.mainLayout = (RelativeLayout) findViewById(R.id.activitymainRelativeLayout1);
|
||||
if (mViewHolder.mainLayout == null) LogUtils.e(TAG, "initViewHolder: mainLayout绑定失败(ID不匹配)");
|
||||
|
||||
mViewHolder.backgroundView = (BackgroundView) findViewById(R.id.fragmentmainviewBackgroundView1);
|
||||
if (mViewHolder.backgroundView == null) LogUtils.e(TAG, "initViewHolder: backgroundView绑定失败(ID不匹配)");
|
||||
|
||||
mViewHolder.llLeftSeekBar = (LinearLayout) findViewById(R.id.fragmentmainviewLinearLayout1);
|
||||
mViewHolder.llRightSeekBar = (LinearLayout) findViewById(R.id.fragmentmainviewLinearLayout2);
|
||||
mViewHolder.cbIsEnableChargeReminder = (CheckBox) findViewById(R.id.fragmentmainviewCheckBox1);
|
||||
mViewHolder.cbIsEnableUsegeReminder = (CheckBox) findViewById(R.id.fragmentmainviewCheckBox2);
|
||||
mViewHolder.swIsEnableService = (Switch) findViewById(R.id.fragmentandroidviewSwitch1);
|
||||
mViewHolder.tvTips = (TextView) findViewById(R.id.fragmentandroidviewTextView1);
|
||||
mViewHolder.tvChargeReminderValue = (TextView) findViewById(R.id.fragmentandroidviewTextView2);
|
||||
mViewHolder.tvUsegeReminderValue = (TextView) findViewById(R.id.fragmentandroidviewTextView3);
|
||||
mViewHolder.tvCurrentValue = (TextView) findViewById(R.id.fragmentandroidviewTextView4);
|
||||
mViewHolder.chargeReminderSeekBar = (VerticalSeekBar) findViewById(R.id.fragmentandroidviewVerticalSeekBar1);
|
||||
mViewHolder.usegeReminderSeekBar = (VerticalSeekBar) findViewById(R.id.fragmentandroidviewVerticalSeekBar2);
|
||||
mViewHolder.ivCurrentBattery = (ImageView) findViewById(R.id.fragmentandroidviewImageView1);
|
||||
mViewHolder.ivChargeReminderBattery = (ImageView) findViewById(R.id.fragmentandroidviewImageView3);
|
||||
mViewHolder.ivUsegeReminderBattery = (ImageView) findViewById(R.id.fragmentandroidviewImageView2);
|
||||
mAdsViewStub = (ViewStub) findViewById(R.id.stub_ads_banner);
|
||||
mToolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化核心工具类(保持原有)
|
||||
*/
|
||||
private void initCoreUtilsAsync() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mApplication = (App) getApplication();
|
||||
mAppConfigUtils = App.getAppConfigUtils(getActivity());
|
||||
mBgSourceUtils = BackgroundSourceUtils.getInstance(getActivity());
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (getActivity() != null) {
|
||||
mDrawableFrame = getActivity().getDrawable(R.drawable.bg_frame);
|
||||
}
|
||||
setViewData();
|
||||
setViewListener();
|
||||
checkServiceAsync();
|
||||
if (_mHandler != null) {
|
||||
_mHandler.sendEmptyMessage(MSG_LOAD_BACKGROUND);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化首屏核心视图(保持原有)
|
||||
*/
|
||||
private void initCriticalView() {
|
||||
_mMainActivity = this;
|
||||
setSupportActionBar(mToolbar);
|
||||
if (mToolbar != null) {
|
||||
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟加载非首屏核心视图(保持原有)
|
||||
*/
|
||||
private void loadNonCriticalViewDelayed() {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isFinishing() || isDestroyed()) return;
|
||||
loadAdsView();
|
||||
}
|
||||
}, DELAY_LOAD_NON_CRITICAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载广告视图(保持原有)
|
||||
*/
|
||||
private void loadAdsView() {
|
||||
if (mAdsViewStub == null) return;
|
||||
if (mADsBannerView == null) {
|
||||
View adsView = mAdsViewStub.inflate();
|
||||
mADsBannerView = (ADsBannerView) adsView.findViewById(R.id.adsbanner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查服务状态(保持原有)
|
||||
*/
|
||||
private void checkServiceAsync() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mAppConfigUtils == null || isFinishing() || isDestroyed()) return;
|
||||
if (mAppConfigUtils.getIsEnableService()
|
||||
&& !ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName())) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Intent intent = new Intent(getActivity(), ControlCenterService.class);
|
||||
getActivity().startForegroundService(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化全局Handler(保持原有)
|
||||
*/
|
||||
private void initGlobalHandler() {
|
||||
if (_mHandler == null) {
|
||||
_mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_RELOAD_APPCONFIG:
|
||||
if (_mMainActivity != null && _mMainActivity.mViewHolder != null) {
|
||||
_mMainActivity.setViewData();
|
||||
}
|
||||
break;
|
||||
case MSG_CURRENTVALUEBATTERY:
|
||||
if (_mMainActivity != null && _mMainActivity.mViewHolder != null) {
|
||||
_mMainActivity.setCurrentValueBattery(msg.arg1);
|
||||
}
|
||||
break;
|
||||
case MSG_LOAD_BACKGROUND:
|
||||
if (_mMainActivity != null) {
|
||||
_mMainActivity.reloadBackground();
|
||||
_mMainActivity.setBackgroundColor();
|
||||
}
|
||||
break;
|
||||
}
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 内部监听类(保持原有)
|
||||
class ChargeReminderSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (mViewHolder == null || mChargeReminderValueBatteryDrawable == null || mViewHolder.tvChargeReminderValue == null) return;
|
||||
mViewHolder.tvChargeReminderValue.setText(String.valueOf(progress) + "%");
|
||||
mChargeReminderValueBatteryDrawable.setValue(progress);
|
||||
mChargeReminderValueBatteryDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
if (mAppConfigUtils == null || mViewHolder == null || mViewHolder.tvChargeReminderValue == null) return;
|
||||
int nChargeReminderValue = ((VerticalSeekBar) seekBar)._mnProgress;
|
||||
mAppConfigUtils.setChargeReminderValue(nChargeReminderValue);
|
||||
mViewHolder.tvChargeReminderValue.setText(String.valueOf(nChargeReminderValue) + "%");
|
||||
}
|
||||
}
|
||||
|
||||
class UsegeReminderSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (mViewHolder == null || mUsegeReminderValueBatteryDrawable == null || mViewHolder.tvUsegeReminderValue == null) return;
|
||||
mViewHolder.tvUsegeReminderValue.setText(String.valueOf(progress) + "%");
|
||||
mUsegeReminderValueBatteryDrawable.setValue(progress);
|
||||
mUsegeReminderValueBatteryDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
if (mAppConfigUtils == null || mViewHolder == null || mViewHolder.tvUsegeReminderValue == null) return;
|
||||
int nUsegeReminderValue = ((VerticalSeekBar) seekBar)._mnProgress;
|
||||
mAppConfigUtils.setUsegeReminderValue(nUsegeReminderValue);
|
||||
mViewHolder.tvUsegeReminderValue.setText(String.valueOf(nUsegeReminderValue) + "%");
|
||||
}
|
||||
}
|
||||
|
||||
// 静态工具方法(保持原有)
|
||||
public static void relaodAppConfigs() {
|
||||
if (_mHandler != null) {
|
||||
_mHandler.sendMessage(_mHandler.obtainMessage(MSG_RELOAD_APPCONFIG));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendMsgCurrentValueBattery(int value) {
|
||||
if (_mHandler != null) {
|
||||
Message msg = _mHandler.obtainMessage(MSG_CURRENTVALUEBATTERY);
|
||||
msg.arg1 = value;
|
||||
_mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private String getRealPathFromURI(Uri contentUri) {
|
||||
String[] proj = {MediaStore.MediaColumns.DATA};
|
||||
Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
|
||||
@@ -297,17 +619,12 @@ public class MainActivity extends WinBoLLActivity {
|
||||
String path = cursor.getString(nColumnIndex);
|
||||
cursor.close();
|
||||
return path;
|
||||
} else {
|
||||
LogUtils.d(TAG, "getRealPathFromURI nColumnIndex is -1.");
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成默认APP信息(用于关于页面)
|
||||
*/
|
||||
APPInfo genDefaultAPPInfo() {
|
||||
String szBranchName = "powerbell";
|
||||
APPInfo appInfo = new APPInfo();
|
||||
@@ -323,314 +640,5 @@ public class MainActivity extends WinBoLLActivity {
|
||||
appInfo.setAppAPKFolderName("PowerBell");
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
|
||||
// ======================== 内部初始化辅助方法(私有,统一放在最后)========================
|
||||
/**
|
||||
* 初始化视图控件(findViewById)
|
||||
*/
|
||||
private void initView() {
|
||||
mAppConfigUtils = App.getAppConfigUtils(getActivity());
|
||||
mBgSourceUtils = BackgroundSourceUtils.getInstance(getActivity());
|
||||
|
||||
mADsBannerView = findViewById(R.id.adsbanner);
|
||||
mToolbar = findViewById(R.id.toolbar);
|
||||
|
||||
// 获取指定ID的View实例
|
||||
mBackgroundView = findViewById(R.id.fragmentmainviewBackgroundView1);
|
||||
|
||||
loadBackground();
|
||||
/*final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1);
|
||||
|
||||
// 注册OnGlobalLayoutListener
|
||||
mainImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
// 获取宽度和高度
|
||||
int width = mainImageView.getMeasuredWidth();
|
||||
int height = mainImageView.getMeasuredHeight();
|
||||
|
||||
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity());
|
||||
BackgroundPictureBean bean = utils.loadBackgroundPictureBean();
|
||||
bean.setBackgroundWidth(width);
|
||||
bean.setBackgroundHeight(height);
|
||||
utils.saveData();
|
||||
// 移除监听器以避免内存泄漏
|
||||
mainImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
});*/
|
||||
|
||||
mDrawableFrame = getActivity().getDrawable(R.drawable.bg_frame);
|
||||
mllLeftSeekBar = (LinearLayout) findViewById(R.id.fragmentmainviewLinearLayout1);
|
||||
mllRightSeekBar = (LinearLayout) findViewById(R.id.fragmentmainviewLinearLayout2);
|
||||
|
||||
// 初始化充电电量提醒设置控件
|
||||
mtvChargeReminderValue = (TextView) findViewById(R.id.fragmentandroidviewTextView2);
|
||||
mChargeReminderSeekBar = (VerticalSeekBar) findViewById(R.id.fragmentandroidviewVerticalSeekBar1);
|
||||
mcbIsEnableChargeReminder = findViewById(R.id.fragmentmainviewCheckBox1);
|
||||
|
||||
// 初始化耗电电量提醒设置控件
|
||||
mtvUsegeReminderValue = (TextView) findViewById(R.id.fragmentandroidviewTextView3);
|
||||
mUsegeReminderSeekBar = (VerticalSeekBar) findViewById(R.id.fragmentandroidviewVerticalSeekBar2);
|
||||
mcbIsEnableUsegeReminder = findViewById(R.id.fragmentmainviewCheckBox2);
|
||||
|
||||
// 初始化现在电量显示控件
|
||||
mtvCurrentValue = (TextView) findViewById(R.id.fragmentandroidviewTextView4);
|
||||
|
||||
// 初始化服务总开关
|
||||
mswIsEnableService = (Switch) findViewById(R.id.fragmentandroidviewSwitch1);
|
||||
mtvTips = findViewById(R.id.fragmentandroidviewTextView1);
|
||||
|
||||
// 设置视图显示数据
|
||||
setViewData();
|
||||
// 设置视图控件响应
|
||||
setViewListener();
|
||||
|
||||
// 注册一个广播接收
|
||||
//mMainActivityReceiver = new MainActivityReceiver(this);
|
||||
//mMainActivityReceiver.registerAction();
|
||||
|
||||
// 启动的时候检查一下服务
|
||||
if (mAppConfigUtils.getIsEnableService()
|
||||
&& ServiceUtils.isServiceAlive(getActivity(), ControlCenterService.class.getName()) == false) {
|
||||
// 如果配置了服务启动,服务没有启动
|
||||
// 就启动服务
|
||||
Intent intent = new Intent(getActivity(), ControlCenterService.class);
|
||||
getActivity().startForegroundService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
void loadBackground() {
|
||||
BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean();
|
||||
mBackgroundView.loadBackgroundBean(bean);
|
||||
}
|
||||
|
||||
void setViewData() {
|
||||
int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue();
|
||||
int nUsegeReminderValue = mAppConfigUtils.getUsegeReminderValue();
|
||||
int nCurrentValue = mAppConfigUtils.getCurrentValue();
|
||||
|
||||
mllLeftSeekBar.setBackground(mDrawableFrame);
|
||||
mllRightSeekBar.setBackground(mDrawableFrame);
|
||||
|
||||
// 初始化电量图
|
||||
mCurrentValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorCurrent));
|
||||
mCurrentValueBatteryDrawable.setValue(mAppConfigUtils.getCurrentValue());
|
||||
mCurrentValueBatteryImageView = findViewById(R.id.fragmentandroidviewImageView1);
|
||||
mCurrentValueBatteryImageView.setImageDrawable(mCurrentValueBatteryDrawable);
|
||||
|
||||
// 初始化充电电量提醒图
|
||||
mChargeReminderValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorCharge));
|
||||
mChargeReminderValueBatteryDrawable.setValue(nChargeReminderValue);
|
||||
mChargeReminderValueBatteryImageView = findViewById(R.id.fragmentandroidviewImageView3);
|
||||
mChargeReminderValueBatteryImageView.setImageDrawable(mChargeReminderValueBatteryDrawable);
|
||||
|
||||
// 初始化耗电电量提醒图
|
||||
mUsegeReminderValueBatteryDrawable = new BatteryDrawable(getActivity().getColor(R.color.colorUsege));
|
||||
mUsegeReminderValueBatteryDrawable.setValue(nUsegeReminderValue);
|
||||
mUsegeReminderValueBatteryImageView = findViewById(R.id.fragmentandroidviewImageView2);
|
||||
mUsegeReminderValueBatteryImageView.setImageDrawable(mUsegeReminderValueBatteryDrawable);
|
||||
|
||||
// 初始化充电电量提醒设置控件
|
||||
mtvChargeReminderValue.setTextColor(getActivity().getColor(R.color.colorCharge));
|
||||
//LogUtils.d(TAG, "Color.YELLOW is " + Integer.toString(mApplication.getColor(R.color.colorCharge)));
|
||||
mtvChargeReminderValue.setText(Integer.toString(nChargeReminderValue) + "%");
|
||||
mChargeReminderSeekBar.setProgress(nChargeReminderValue);
|
||||
mcbIsEnableChargeReminder.setChecked(mAppConfigUtils.getIsEnableChargeReminder());
|
||||
|
||||
// 初始化耗电电量提醒设置控件
|
||||
mtvUsegeReminderValue.setTextColor(getActivity().getColor(R.color.colorUsege));
|
||||
mtvUsegeReminderValue.setText(Integer.toString(nUsegeReminderValue) + "%");
|
||||
mUsegeReminderSeekBar.setProgress(nUsegeReminderValue);
|
||||
mcbIsEnableUsegeReminder.setChecked(mAppConfigUtils.getIsEnableUsegeReminder());
|
||||
|
||||
// 初始化现在电量显示控件
|
||||
mtvCurrentValue.setTextColor(getActivity().getColor(R.color.colorCurrent));
|
||||
mtvCurrentValue.setText(Integer.toString(nCurrentValue) + "%");
|
||||
|
||||
// 初始化服务总开关
|
||||
mswIsEnableService.setChecked(mAppConfigUtils.getIsEnableService());
|
||||
if (mAppConfigUtils.getIsEnableService()) {
|
||||
//LogUtils.d(TAG, "mApplication.getIsEnableService() " + Boolean.toString(mAppConfigUtils.getIsEnableService()));
|
||||
ControlCenterService.startControlCenterService(getActivity());
|
||||
} else {
|
||||
//LogUtils.d(TAG, "mApplication.getIsEnableService() " + Boolean.toString(mAppConfigUtils.getIsEnableService()));
|
||||
ControlCenterService.stopControlCenterService(getActivity());
|
||||
}
|
||||
mswIsEnableService.setText(getString(R.string.txt_aboveswitch));
|
||||
mtvTips.setText(getString(R.string.txt_aboveswitchtips));
|
||||
|
||||
}
|
||||
|
||||
void setViewListener() {
|
||||
// 初始化充电电量提醒设置控件
|
||||
mChargeReminderSeekBarChangeListener = new ChargeReminderSeekBarChangeListener();
|
||||
mChargeReminderSeekBar.setOnSeekBarChangeListener(mChargeReminderSeekBarChangeListener);
|
||||
mcbIsEnableChargeReminder.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "setIsEnableChargeReminder");
|
||||
mAppConfigUtils.setIsEnableChargeReminder(mcbIsEnableChargeReminder.isChecked());
|
||||
//ControlCenterService.updateIsEnableChargeReminder(mcbIsEnableChargeReminder.isChecked());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 初始化耗电电量提醒设置控件
|
||||
mUsegeReminderSeekBarChangeListener = new UsegeReminderSeekBarChangeListener();
|
||||
mUsegeReminderSeekBar.setOnSeekBarChangeListener(mUsegeReminderSeekBarChangeListener);
|
||||
mcbIsEnableUsegeReminder.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "setIsEnableUsegeReminder");
|
||||
mAppConfigUtils.setIsEnableUsegeReminder(mcbIsEnableUsegeReminder.isChecked());
|
||||
//ControlCenterService.updateIsEnableUsegeReminder(mcbIsEnableUsegeReminder.isChecked());
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化服务总开关
|
||||
mswIsEnableService.setOnClickListener(new CompoundButton.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mAppConfigUtils.setIsEnableService(getActivity(), mswIsEnableService.isChecked());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setCurrentValueBattery(int value) {
|
||||
//LogUtils.d(TAG, "setCurrentValueBattery");
|
||||
mtvCurrentValue.setText(Integer.toString(value) + "%");
|
||||
mCurrentValueBatteryDrawable.setValue(value);
|
||||
mCurrentValueBatteryDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void reloadBackground() {
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity());
|
||||
utils.loadSettings();
|
||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||
mBackgroundView.loadBackgroundBean(bean);
|
||||
}
|
||||
|
||||
static Handler _mHandler = new Handler(){
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_RELOAD_APPCONFIG : {
|
||||
if (_mMainActivity != null) {
|
||||
_mMainActivity.setViewData();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_CURRENTVALUEBATTERY : {
|
||||
if (_mMainActivity != null) {
|
||||
_mMainActivity.setCurrentValueBattery(msg.arg1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static void relaodAppConfigs() {
|
||||
if (_mHandler != null) {
|
||||
_mHandler.sendMessage(_mHandler.obtainMessage(MSG_RELOAD_APPCONFIG));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendMsgCurrentValueBattery(int value) {
|
||||
if (_mHandler != null) {
|
||||
Message msg = _mHandler.obtainMessage(MSG_CURRENTVALUEBATTERY);
|
||||
msg.arg1 = value;
|
||||
_mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化实例(Application、工具类等)
|
||||
*/
|
||||
private void initInstance() {
|
||||
_mMainActivity = this;
|
||||
mApplication = (App) getApplication();
|
||||
// 初始化工具栏
|
||||
setSupportActionBar(mToolbar);
|
||||
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化主Fragment(添加并显示)
|
||||
*/
|
||||
// private void initFragment() {
|
||||
// if (mMainViewFragment == null) {
|
||||
// FragmentTransaction tx = getFragmentManager().beginTransaction();
|
||||
// mMainViewFragment = new MainViewFragment();
|
||||
// tx.add(R.id.activitymainFrameLayout1, mMainViewFragment, MainViewFragment.TAG);
|
||||
// tx.commit();
|
||||
// }
|
||||
// showFragment(mMainViewFragment);
|
||||
// }
|
||||
|
||||
class ChargeReminderSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
//LogUtils.d(TAG, "call onProgressChanged");
|
||||
int nChargeReminderValue = progress;
|
||||
mtvChargeReminderValue.setText(Integer.toString(nChargeReminderValue) + "%");
|
||||
mChargeReminderValueBatteryDrawable.setValue(nChargeReminderValue);
|
||||
mChargeReminderValueBatteryDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStartTrackingTouch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStopTrackingTouch");
|
||||
//取得当前进度条的刻度
|
||||
int nChargeReminderValue = ((VerticalSeekBar)seekBar)._mnProgress;
|
||||
|
||||
mAppConfigUtils.setChargeReminderValue(nChargeReminderValue);
|
||||
mtvChargeReminderValue.setText(Integer.toString(nChargeReminderValue) + "%");
|
||||
//ControlCenterService.updateChargeReminderValue(nChargeReminderValue);
|
||||
}
|
||||
}
|
||||
|
||||
class UsegeReminderSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
//LogUtils.d(TAG, "call onProgressChanged");
|
||||
int nUsegeReminderValue = progress;
|
||||
mtvUsegeReminderValue.setText(Integer.toString(nUsegeReminderValue) + "%");
|
||||
mUsegeReminderValueBatteryDrawable.setValue(nUsegeReminderValue);
|
||||
mUsegeReminderValueBatteryDrawable.invalidateSelf();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStartTrackingTouch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
//LogUtils.d(TAG, "call onStopTrackingTouch");
|
||||
//取得当前进度条的刻度
|
||||
int nUsegeReminderValue = ((VerticalSeekBar)seekBar)._mnProgress;
|
||||
LogUtils.d(TAG, "nUsegeReminderValue is " + Integer.toString(nUsegeReminderValue));
|
||||
//LogUtils.d(TAG, "mPowerReminder is " + mApplication);
|
||||
mAppConfigUtils.setUsegeReminderValue(nUsegeReminderValue);
|
||||
//LogUtils.d(TAG, "opopopopopopopop");
|
||||
mtvUsegeReminderValue.setText(Integer.toString(nUsegeReminderValue) + "%");
|
||||
//ControlCenterService.updateUsegeReminderValue(nUsegeReminderValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.model.BatteryInfoBean;
|
||||
import cc.winboll.studio.powerbell.models.BatteryInfoBean;
|
||||
import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver;
|
||||
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.StringUtils;
|
||||
|
||||
@@ -26,7 +26,7 @@ import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.activities.BackgroundSettingsActivity;
|
||||
import cc.winboll.studio.powerbell.activities.PixelPickerActivity;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.models.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
@@ -18,6 +18,7 @@ import cc.winboll.studio.powerbell.utils.PermissionUtils;
|
||||
public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
|
||||
public static final String TAG = "SettingsActivity";
|
||||
private static final int REQUEST_READ_MEDIA_IMAGES = 1001;
|
||||
|
||||
private Toolbar mToolbar;
|
||||
|
||||
@@ -52,8 +53,15 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit
|
||||
|
||||
public void onCheckPermission(View view) {
|
||||
//ToastUtils.show("onCheckPermission");
|
||||
if (PermissionUtils.getInstance().checkAndRequestStoragePermission(this)) {
|
||||
ToastUtils.show("【权限检查】存储权限已全部获取");
|
||||
PermissionUtils.getInstance().checkAndRequestMediaImagesPermission(this, REQUEST_READ_MEDIA_IMAGES);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == REQUEST_READ_MEDIA_IMAGES) {
|
||||
PermissionUtils.getInstance().handleStoragePermissionResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.adapters.BatteryAdapter;
|
||||
import cc.winboll.studio.powerbell.model.BatteryData;
|
||||
import cc.winboll.studio.powerbell.models.BatteryData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -2,22 +2,19 @@ package cc.winboll.studio.powerbell.dialogs;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.activities.BackgroundSettingsActivity;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
||||
import cc.winboll.studio.powerbell.utils.FileUtils;
|
||||
import cc.winboll.studio.powerbell.utils.UriUtil;
|
||||
import cc.winboll.studio.powerbell.utils.UriUtils;
|
||||
import cc.winboll.studio.powerbell.views.BackgroundView;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -29,21 +26,25 @@ public class BackgroundPicturePreviewDialog extends Dialog {
|
||||
public static final String TAG = "BackgroundPicturePreviewDialog";
|
||||
|
||||
Context mContext;
|
||||
BackgroundSourceUtils mBackgroundPictureUtils;
|
||||
//BackgroundSourceUtils mBackgroundPictureUtils;
|
||||
Button dialogbackgroundpicturepreviewButton1;
|
||||
Button dialogbackgroundpicturepreviewButton2;
|
||||
String mszPreReceivedFileName;
|
||||
//String mszPreReceivedFileName;
|
||||
IOnRecivedPictureListener mIOnRecivedPictureListener;
|
||||
Uri mUriRecivedPicture;
|
||||
BackgroundView mBackgroundView;
|
||||
|
||||
public BackgroundPicturePreviewDialog(Context context) {
|
||||
public BackgroundPicturePreviewDialog(Context context, IOnRecivedPictureListener iOnRecivedPictureListener) {
|
||||
super(context);
|
||||
setContentView(R.layout.dialog_backgroundpicturepreview);
|
||||
initEnv();
|
||||
mIOnRecivedPictureListener = iOnRecivedPictureListener;
|
||||
//initEnv();
|
||||
|
||||
mContext = context;
|
||||
mBackgroundPictureUtils = BackgroundSourceUtils.getInstance(mContext);
|
||||
//mBackgroundPictureUtils = BackgroundSourceUtils.getInstance(mContext);
|
||||
|
||||
ImageView imageView = findViewById(R.id.dialogbackgroundpicturepreviewImageView1);
|
||||
copyAndViewRecivePicture(imageView);
|
||||
mBackgroundView = findViewById(R.id.backgroundview);
|
||||
previewRecivedPicture();
|
||||
|
||||
dialogbackgroundpicturepreviewButton1 = findViewById(R.id.dialogbackgroundpicturepreviewButton1);
|
||||
dialogbackgroundpicturepreviewButton1.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -53,6 +54,7 @@ public class BackgroundPicturePreviewDialog extends Dialog {
|
||||
// 跳转到主窗口
|
||||
Intent i = new Intent(mContext, MainActivity.class);
|
||||
mContext.startActivity(i);
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,79 +64,77 @@ public class BackgroundPicturePreviewDialog extends Dialog {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 使用分享到的图片
|
||||
//
|
||||
//LogUtils.d(TAG, "mszReceivedFileName : " + mszReceivedFileName);
|
||||
((IOnRecivedPictureListener)mContext).onAcceptRecivedPicture(mszPreReceivedFileName);
|
||||
mIOnRecivedPictureListener.onAcceptRecivedPicture(mUriRecivedPicture);
|
||||
// 关闭对话框
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void initEnv() {
|
||||
LogUtils.d(TAG, "initEnv()");
|
||||
mszPreReceivedFileName = "PreReceived.data";
|
||||
}
|
||||
// void initEnv() {
|
||||
// LogUtils.d(TAG, "initEnv()");
|
||||
// mszPreReceivedFileName = "PreReceived.data";
|
||||
// }
|
||||
|
||||
void copyAndViewRecivePicture(ImageView imageView) {
|
||||
//AppConfigUtils appConfigUtils = AppConfigUtils.getInstance((GlobalApplication)mContext.getApplicationContext());
|
||||
void previewRecivedPicture() {
|
||||
BackgroundSettingsActivity activity = ((BackgroundSettingsActivity)mContext);
|
||||
|
||||
//取出文件uri
|
||||
Uri uri = activity.getIntent().getData();
|
||||
if (uri == null) {
|
||||
uri = activity.getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
mUriRecivedPicture = activity.getIntent().getData();
|
||||
if (mUriRecivedPicture == null) {
|
||||
mUriRecivedPicture = activity.getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
}
|
||||
//获取文件真实地址
|
||||
String szSrcImage = UriUtil.getFilePathFromUri(mContext, uri);
|
||||
String szSrcImage = UriUtils.getFilePathFromUri(mContext, mUriRecivedPicture);
|
||||
if (TextUtils.isEmpty(szSrcImage)) {
|
||||
Toast.makeText(mContext, "接收到的文件为空。", Toast.LENGTH_SHORT).show();
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
File fSrcImage = new File(szSrcImage);
|
||||
//mszPreReceivedFileName = DateUtils.getDateNowString() + "-" + fSrcImage.getName();
|
||||
File mfPreReceivedPhoto = new File(BackgroundSourceUtils.getInstance(mContext).getBackgroundSourceDirPath(), mszPreReceivedFileName);
|
||||
// 复制源图片到剪裁文件
|
||||
try {
|
||||
FileUtils.copyFileUsingFileChannels(fSrcImage, mfPreReceivedPhoto);
|
||||
LogUtils.d(TAG, "copyFileUsingFileChannels");
|
||||
Drawable drawable = Drawable.createFromPath(mfPreReceivedPhoto.getPath());
|
||||
imageView.setBackground(drawable);
|
||||
//LogUtils.d(TAG, "mszPreReceivedFileName : " + mszPreReceivedFileName);
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
mBackgroundView.loadImage(szSrcImage);
|
||||
//
|
||||
// File fSrcImage = new File(szSrcImage);
|
||||
// //mszPreReceivedFileName = DateUtils.getDateNowString() + "-" + fSrcImage.getName();
|
||||
// File mfPreReceivedPhoto = new File(BackgroundSourceUtils.getInstance(mContext).getBackgroundSourceDirPath(), mszPreReceivedFileName);
|
||||
// // 复制源图片到剪裁文件
|
||||
// try {
|
||||
// FileUtils.copyFileUsingFileChannels(fSrcImage, mfPreReceivedPhoto);
|
||||
// LogUtils.d(TAG, "copyFileUsingFileChannels");
|
||||
// Drawable drawable = Drawable.createFromPath(mfPreReceivedPhoto.getPath());
|
||||
// imageView.setBackground(drawable);
|
||||
// //LogUtils.d(TAG, "mszPreReceivedFileName : " + mszPreReceivedFileName);
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 创建图片背景图片目录
|
||||
//
|
||||
boolean createBackgroundFolder2(String szBackgroundFolder) {
|
||||
// 文件路径参数为空值或无效值时返回false.
|
||||
if (szBackgroundFolder == null | szBackgroundFolder.equals("")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "Background Folder Is : " + szBackgroundFolder);
|
||||
File f = new File(szBackgroundFolder);
|
||||
if (f.exists()) {
|
||||
if (f.isDirectory()) {
|
||||
return true;
|
||||
} else {
|
||||
// 工作路径不是一个目录
|
||||
LogUtils.d(TAG, "createImageWorkFolder() error : szImageCacheFolder isDirectory return false. -->" + szBackgroundFolder);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return f.mkdirs();
|
||||
}
|
||||
}
|
||||
// boolean createBackgroundFolder2(String szBackgroundFolder) {
|
||||
// // 文件路径参数为空值或无效值时返回false.
|
||||
// if (szBackgroundFolder == null | szBackgroundFolder.equals("")) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// LogUtils.d(TAG, "Background Folder Is : " + szBackgroundFolder);
|
||||
// File f = new File(szBackgroundFolder);
|
||||
// if (f.exists()) {
|
||||
// if (f.isDirectory()) {
|
||||
// return true;
|
||||
// } else {
|
||||
// // 工作路径不是一个目录
|
||||
// LogUtils.d(TAG, "createImageWorkFolder() error : szImageCacheFolder isDirectory return false. -->" + szBackgroundFolder);
|
||||
// return false;
|
||||
// }
|
||||
// } else {
|
||||
// return f.mkdirs();
|
||||
// }
|
||||
// }
|
||||
|
||||
public interface IOnRecivedPictureListener {
|
||||
void onAcceptRecivedPicture(String szBackgroundFileName);
|
||||
void onAcceptRecivedPicture(Uri uriRecivedPicture);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.activities.PixelPickerActivity;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.models.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.powerbell.model;
|
||||
package cc.winboll.studio.powerbell.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -1,16 +1,17 @@
|
||||
package cc.winboll.studio.powerbell.model;
|
||||
package cc.winboll.studio.powerbell.models;
|
||||
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/07/18 11:52:28
|
||||
* @Describe 应用背景图片数据类(存储正式/预览背景配置,支持JSON序列化/反序列化)
|
||||
*/
|
||||
public class BackgroundBean extends BaseBean {
|
||||
public class BackgroundBean extends BaseBean implements Serializable {
|
||||
|
||||
public static final String TAG = "BackgroundPictureBean";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.powerbell.model;
|
||||
package cc.winboll.studio.powerbell.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.powerbell.model;
|
||||
package cc.winboll.studio.powerbell.models;
|
||||
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.powerbell.model;
|
||||
package cc.winboll.studio.powerbell.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.powerbell.model;
|
||||
package cc.winboll.studio.powerbell.models;
|
||||
|
||||
// 应用消息结构
|
||||
//
|
||||
@@ -5,7 +5,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.model.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.models.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BatteryUtils;
|
||||
|
||||
@@ -23,15 +23,15 @@ import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.model.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.model.NotificationMessage;
|
||||
import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler;
|
||||
import cc.winboll.studio.powerbell.models.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.models.NotificationMessage;
|
||||
import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver;
|
||||
import cc.winboll.studio.powerbell.services.AssistantService;
|
||||
import cc.winboll.studio.powerbell.threads.RemindThread;
|
||||
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.NotificationHelper;
|
||||
import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
|
||||
import cc.winboll.studio.powerbell.utils.ServiceUtils;
|
||||
import cc.winboll.studio.powerbell.utils.StringUtils;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ControlCenterService extends Service {
|
||||
AppConfigUtils mAppConfigUtils;
|
||||
AppCacheUtils mAppCacheUtils;
|
||||
// 前台服务通知工具
|
||||
NotificationHelper mNotificationHelper;
|
||||
NotificationManagerUtils mNotificationManagerUtils;
|
||||
Notification notification;
|
||||
RemindThread mRemindThread;
|
||||
ControlCenterServiceHandler mControlCenterServiceHandler;
|
||||
@@ -72,7 +72,7 @@ public class ControlCenterService extends Service {
|
||||
isServiceRunning = false;
|
||||
mAppConfigUtils = App.getAppConfigUtils(this);
|
||||
mAppCacheUtils = App.getAppCacheUtils(this);
|
||||
mNotificationHelper = new NotificationHelper(ControlCenterService.this);
|
||||
mNotificationManagerUtils = new NotificationManagerUtils(ControlCenterService.this);
|
||||
|
||||
|
||||
if (mMyServiceConnection == null) {
|
||||
@@ -101,10 +101,10 @@ public class ControlCenterService extends Service {
|
||||
wakeupAndBindAssistant();
|
||||
// 显示前台通知栏
|
||||
// 在Service中
|
||||
NotificationHelper helper = new NotificationHelper(this);
|
||||
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);
|
||||
NotificationManagerUtils notificationManagerUtils = new NotificationManagerUtils(this);
|
||||
//Intent intent = new Intent(this, MainActivity.class);
|
||||
notificationManagerUtils.startForegroundServiceNotify(ControlCenterService.this, new NotificationMessage(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();
|
||||
@@ -260,9 +260,9 @@ public class ControlCenterService extends Service {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
msg += szRemindMSG;
|
||||
}
|
||||
NotificationHelper helper = new NotificationHelper(ControlCenterService.this);
|
||||
NotificationManagerUtils notificationManagerUtils = new NotificationManagerUtils(ControlCenterService.this);
|
||||
Intent intent = new Intent(ControlCenterService.this, MainActivity.class);
|
||||
helper.showTemporaryNotification(intent, getString(R.string.app_name), msg);
|
||||
notificationManagerUtils.showTempAlertNotify(intent, getString(R.string.app_name), msg);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public class MainUnitTestActivity extends AppCompatActivity {
|
||||
BackgroundView mBackgroundView;
|
||||
// 测试图片路径(用Environment获取,适配低版本,避免硬编码)
|
||||
String szTestSource = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
|
||||
+ "/PowerBell/unittest/2ae9dc9e-7a73-49d4-840a-7ff1712d868c1764798674763.jpeg";
|
||||
+ "/PowerBell/unittest/1764946782079.jpeg";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -90,7 +90,7 @@ public class MainUnitTestActivity extends AppCompatActivity {
|
||||
LogUtils.d(TAG, "【裁剪测试】创建输出目录:" + outputDir.getAbsolutePath());
|
||||
}
|
||||
String dstOutputPath = outputDir.getAbsolutePath()
|
||||
+ "/SelectCompress_2ae9dc9e-7a73-49d4-840a-7ff1712d868c1764798674763.jpeg";
|
||||
+ "/1764946782079-crop.jpeg";
|
||||
|
||||
// 修复3:自由裁剪时比例传0(避免100:100过大导致机型崩溃)
|
||||
ImageCropUtils.startImageCrop(
|
||||
@@ -161,7 +161,7 @@ public class MainUnitTestActivity extends AppCompatActivity {
|
||||
// 裁剪完成后回收权限
|
||||
if (requestCode == REQUEST_CROP_IMAGE) {
|
||||
String dstOutputPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
|
||||
+ "/PowerBell/unittest/SelectCompress_2ae9dc9e-7a73-49d4-840a-7ff1712d868c1764798674763.jpeg";
|
||||
+ "/PowerBell/unittest/1764946782079-crop.jpeg";
|
||||
//Uri outputUri = ImageCropUtils.getFileProviderUriPublic(this, new File(dstOutputPath));
|
||||
//ImageCropUtils.releaseCropPermission(this, outputUri);
|
||||
mBackgroundView.loadImage(dstOutputPath);
|
||||
|
||||
@@ -2,7 +2,7 @@ package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.model.BatteryInfoBean;
|
||||
import cc.winboll.studio.powerbell.models.BatteryInfoBean;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AppCacheUtils {
|
||||
|
||||
@@ -5,8 +5,8 @@ import android.content.Context;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.model.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.model.ControlCenterServiceBean;
|
||||
import cc.winboll.studio.powerbell.models.AppConfigBean;
|
||||
import cc.winboll.studio.powerbell.models.ControlCenterServiceBean;
|
||||
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
|
||||
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/12/11 09:14
|
||||
* @Describe Assets 目录拷贝工具类
|
||||
* 支持将 assets/images/ 下所有文件、子目录拷贝到指定路径
|
||||
*/
|
||||
public class AssetsCopyUtils {
|
||||
public static final String TAG = "AssetsCopyUtils";
|
||||
private static final int BUFFER_SIZE = 1024 * 8;
|
||||
|
||||
/**
|
||||
* 拷贝 assets/images/ 目录到指定目标目录
|
||||
* @param context 上下文
|
||||
* @param targetDirPath 目标目录完整路径(如 /sdcard/PowerBell/assets_images)
|
||||
* @return 拷贝是否成功
|
||||
*/
|
||||
public static boolean copyAssetsImagesToDir(Context context, String targetDirPath) {
|
||||
// 拷贝 assets/images 根目录
|
||||
return copyAssetsDirToDir(context, "images", targetDirPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归拷贝 assets 下指定目录到目标目录
|
||||
* @param context 上下文
|
||||
* @param assetsDir assets 下的源目录(如 "images"、"images/subdir")
|
||||
* @param targetDirPath 目标目录完整路径
|
||||
* @return 拷贝是否成功
|
||||
*/
|
||||
public static boolean copyAssetsDirToDir(Context context, String assetsDir, String targetDirPath) {
|
||||
File targetDir = new File(targetDirPath);
|
||||
// 创建目标目录(含多级父目录)
|
||||
if (!targetDir.exists() && !targetDir.mkdirs()) {
|
||||
Log.e(TAG, "创建目标目录失败:" + targetDirPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取 assets 目录下的文件/子目录列表
|
||||
String[] fileList = context.getAssets().list(assetsDir);
|
||||
if (fileList == null || fileList.length == 0) {
|
||||
Log.d(TAG, "assets 目录为空:" + assetsDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String fileName : fileList) {
|
||||
String assetsFilePath = assetsDir + File.separator + fileName;
|
||||
String targetFilePath = targetDirPath + File.separator + fileName;
|
||||
|
||||
// 判断当前项是文件还是子目录
|
||||
String[] subFileList = context.getAssets().list(assetsFilePath);
|
||||
if (subFileList != null && subFileList.length > 0) {
|
||||
// 是子目录,递归拷贝
|
||||
if (!copyAssetsDirToDir(context, assetsFilePath, targetFilePath)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 是文件,直接拷贝
|
||||
if (!copyAssetsFileToDir(context, assetsFilePath, targetFilePath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "assets 目录拷贝完成:" + assetsDir + " -> " + targetDirPath);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "拷贝 assets 目录异常:" + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝 assets 下单个文件到指定路径
|
||||
* @param context 上下文
|
||||
* @param assetsFilePath assets 下的文件路径(如 "images/cloud.png")
|
||||
* @param targetFilePath 目标文件完整路径
|
||||
* @return 拷贝是否成功
|
||||
*/
|
||||
public static boolean copyAssetsFileToDir(Context context, String assetsFilePath, String targetFilePath) {
|
||||
InputStream inputStream = null;
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
inputStream = context.getAssets().open(assetsFilePath);
|
||||
File targetFile = new File(targetFilePath);
|
||||
// 覆盖已存在的文件
|
||||
if (targetFile.exists() && !targetFile.delete()) {
|
||||
Log.w(TAG, "覆盖目标文件失败,跳过:" + targetFilePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
outputStream = new FileOutputStream(targetFile);
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
Log.d(TAG, "文件拷贝成功:" + assetsFilePath + " -> " + targetFilePath);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "拷贝文件失败:" + assetsFilePath + ",异常:" + e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
// 关闭流
|
||||
try {
|
||||
if (inputStream != null) inputStream.close();
|
||||
if (outputStream != null) outputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "关闭流异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,206 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/12/11 01:57
|
||||
* @Describe 单例 Bitmap 缓存工具类(Java 7 兼容)
|
||||
* 功能:内存缓存 Bitmap,支持路径关联缓存、全局获取、缓存清空
|
||||
* 特点:1. 单例模式 2. 压缩加载避免OOM 3. 路径- Bitmap 映射 4. 线程安全
|
||||
*/
|
||||
public class BitmapCacheUtils {
|
||||
public static final String TAG = "BitmapCacheUtils";
|
||||
// 最大图片尺寸(适配1080P屏幕,可根据需求调整)
|
||||
private static final int MAX_WIDTH = 1080;
|
||||
private static final int MAX_HEIGHT = 1920;
|
||||
|
||||
// 单例实例(volatile 保证多线程可见性)
|
||||
private static volatile BitmapCacheUtils sInstance;
|
||||
// 路径-Bitmap 缓存容器(内存缓存)
|
||||
private final Map<String, Bitmap> mBitmapCacheMap;
|
||||
|
||||
// 私有构造器(单例模式)
|
||||
private BitmapCacheUtils() {
|
||||
mBitmapCacheMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例(双重校验锁,线程安全)
|
||||
*/
|
||||
public static BitmapCacheUtils getInstance() {
|
||||
if (sInstance == null) {
|
||||
synchronized (BitmapCacheUtils.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new BitmapCacheUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心接口:根据图片路径缓存 Bitmap 到内存
|
||||
* @param imagePath 图片绝对路径
|
||||
* @return 缓存成功的 Bitmap / null(路径无效/文件不存在/解码失败)
|
||||
*/
|
||||
public Bitmap cacheBitmap(String imagePath) {
|
||||
if (TextUtils.isEmpty(imagePath)) {
|
||||
LogUtils.e(TAG, "cacheBitmap: 图片路径为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
File imageFile = new File(imagePath);
|
||||
if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
|
||||
LogUtils.e(TAG, "cacheBitmap: 图片文件无效(不存在/非文件/空文件) - " + imagePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 已缓存则直接返回,避免重复加载
|
||||
if (mBitmapCacheMap.containsKey(imagePath)) {
|
||||
Bitmap cachedBitmap = mBitmapCacheMap.get(imagePath);
|
||||
// 额外校验缓存的Bitmap是否有效
|
||||
if (cachedBitmap != null && !cachedBitmap.isRecycled()) {
|
||||
LogUtils.d(TAG, "cacheBitmap: 图片已缓存,直接返回 - " + imagePath);
|
||||
return cachedBitmap;
|
||||
} else {
|
||||
// 缓存的Bitmap已失效,移除后重新加载
|
||||
mBitmapCacheMap.remove(imagePath);
|
||||
LogUtils.w(TAG, "cacheBitmap: 缓存Bitmap已失效,移除后重新加载 - " + imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
// 压缩加载 Bitmap(避免OOM)
|
||||
Bitmap bitmap = decodeCompressedBitmap(imagePath);
|
||||
if (bitmap != null) {
|
||||
// 存入缓存容器
|
||||
mBitmapCacheMap.put(imagePath, bitmap);
|
||||
LogUtils.d(TAG, "cacheBitmap: 图片缓存成功 - " + imagePath);
|
||||
} else {
|
||||
LogUtils.e(TAG, "cacheBitmap: 图片解码失败 - " + imagePath);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心接口:根据路径获取缓存的 Bitmap
|
||||
* @param imagePath 图片绝对路径
|
||||
* @return 缓存的有效 Bitmap / null(未缓存/已回收)
|
||||
*/
|
||||
public Bitmap getCachedBitmap(String imagePath) {
|
||||
if (TextUtils.isEmpty(imagePath)) {
|
||||
return null;
|
||||
}
|
||||
Bitmap bitmap = mBitmapCacheMap.get(imagePath);
|
||||
// 校验Bitmap是否有效
|
||||
if (bitmap != null && bitmap.isRecycled()) {
|
||||
mBitmapCacheMap.remove(imagePath);
|
||||
return null;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有 Bitmap 缓存(释放内存)
|
||||
*/
|
||||
public void clearAllCache() {
|
||||
synchronized (mBitmapCacheMap) {
|
||||
for (Bitmap bitmap : mBitmapCacheMap.values()) {
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
bitmap.recycle(); // 主动回收 Bitmap
|
||||
}
|
||||
}
|
||||
mBitmapCacheMap.clear();
|
||||
}
|
||||
LogUtils.d(TAG, "clearAllCache: 所有 Bitmap 缓存已清空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定路径的 Bitmap 缓存
|
||||
* @param imagePath 图片绝对路径
|
||||
*/
|
||||
public void removeCachedBitmap(String imagePath) {
|
||||
if (TextUtils.isEmpty(imagePath)) {
|
||||
return;
|
||||
}
|
||||
synchronized (mBitmapCacheMap) {
|
||||
Bitmap bitmap = mBitmapCacheMap.remove(imagePath);
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
bitmap.recycle();
|
||||
LogUtils.d(TAG, "removeCachedBitmap: 移除并回收缓存 - " + imagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩解码 Bitmap(按最大尺寸缩放,避免OOM)
|
||||
* @param imagePath 图片绝对路径
|
||||
* @return 解码后的 Bitmap / null(文件无效/解码失败)
|
||||
*/
|
||||
private Bitmap decodeCompressedBitmap(String imagePath) {
|
||||
// 前置校验:确保文件有效
|
||||
File imageFile = new File(imagePath);
|
||||
if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
|
||||
LogUtils.e(TAG, "decodeCompressedBitmap: 文件无效,跳过解码 - " + imagePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
// 第一步:只获取图片尺寸,不加载像素
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imagePath, options);
|
||||
|
||||
// 校验尺寸是否有效
|
||||
if (options.outWidth <= 0 || options.outHeight <= 0) {
|
||||
LogUtils.e(TAG, "decodeCompressedBitmap: 图片尺寸无效 - " + imagePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算缩放比例
|
||||
int sampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);
|
||||
|
||||
// 第二步:加载压缩后的 Bitmap
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = sampleSize;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存(比ARGB_8888少一半内存)
|
||||
options.inPurgeable = true;
|
||||
options.inInputShareable = true;
|
||||
|
||||
try {
|
||||
return BitmapFactory.decodeFile(imagePath, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
LogUtils.e(TAG, "decodeCompressedBitmap: OOM异常 - " + imagePath);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "decodeCompressedBitmap: 解码异常 - " + imagePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 Bitmap 缩放比例
|
||||
*/
|
||||
private int calculateInSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) {
|
||||
int rawWidth = options.outWidth;
|
||||
int rawHeight = options.outHeight;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (rawWidth > maxWidth || rawHeight > maxHeight) {
|
||||
int halfWidth = rawWidth / 2;
|
||||
int halfHeight = rawHeight / 2;
|
||||
while ((halfWidth / inSampleSize) >= maxWidth && (halfHeight / inSampleSize) >= maxHeight) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
}
|
||||
return inSampleSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.core.content.FileProvider;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.models.BackgroundBean;
|
||||
import com.yalantis.ucrop.UCrop;
|
||||
import com.yalantis.ucrop.UCropActivity;
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/22 04:39:40
|
||||
* @Describe 通知工具类
|
||||
*/
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.widget.RemoteViews;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
|
||||
public class NotificationHelper {
|
||||
public static final String TAG = "NotificationHelper";
|
||||
|
||||
// 渠道ID和名称
|
||||
private static final String CHANNEL_ID_FOREGROUND = "foreground_channel";
|
||||
private static final String CHANNEL_NAME_FOREGROUND = "Foreground Service";
|
||||
private static final String CHANNEL_ID_TEMPORARY = "temporary_channel";
|
||||
private static final String CHANNEL_NAME_TEMPORARY = "Temporary Notifications";
|
||||
|
||||
// 通知ID
|
||||
public static final int FOREGROUND_NOTIFICATION_ID = 1001;
|
||||
public static final int TEMPORARY_NOTIFICATION_ID = 2001;
|
||||
|
||||
private final Context mContext;
|
||||
private final NotificationManager mNotificationManager;
|
||||
|
||||
public NotificationHelper(Context context) {
|
||||
mContext = context;
|
||||
mNotificationManager = context.getSystemService(NotificationManager.class);
|
||||
createNotificationChannels();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createForegroundChannel();
|
||||
createTemporaryChannel();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createForegroundChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_FOREGROUND,
|
||||
CHANNEL_NAME_FOREGROUND,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription("Persistent service notifications");
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(false);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createTemporaryChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_TEMPORARY,
|
||||
CHANNEL_NAME_TEMPORARY,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
channel.setDescription("Temporary alert notifications");
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(new long[]{100, 200, 300, 400});
|
||||
channel.setBypassDnd(true);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
// 显示常驻通知(通常用于前台服务)
|
||||
public Notification showForegroundNotification(Intent intent, String title, String content) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_FOREGROUND)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
//.setContentTitle(title + "\n" + content)
|
||||
.setContentTitle(content)
|
||||
//.setContentText(content)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true)
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification);
|
||||
return notification;
|
||||
}
|
||||
|
||||
// 显示临时通知(自动消失)
|
||||
public void showTemporaryNotification(Intent intent, String title, String content) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.setVibrate(new long[]{100, 200, 300, 400})
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(TEMPORARY_NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
// 创建自定义布局通知(可扩展)
|
||||
public void showCustomNotification(Intent intent, RemoteViews contentView, RemoteViews bigContentView) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContent(contentView)
|
||||
.setCustomBigContentView(bigContentView)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(TEMPORARY_NOTIFICATION_ID + 1, notification);
|
||||
}
|
||||
|
||||
// 取消所有通知
|
||||
public void cancelAllNotifications() {
|
||||
mNotificationManager.cancelAll();
|
||||
}
|
||||
|
||||
// 创建PendingIntent(兼容不同API版本)
|
||||
private PendingIntent createPendingIntent(Intent intent) {
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// flags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
// }
|
||||
return PendingIntent.getActivity(
|
||||
mContext,
|
||||
0,
|
||||
intent,
|
||||
flags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.media.RingtoneManager;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.models.NotificationMessage;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/12/13 20:44
|
||||
* @Describe 全局通知管理工具类(整合所有通知能力,适配API29-30,兼容Java7)
|
||||
*/
|
||||
public class NotificationManagerUtils {
|
||||
// ====================== 常量定义(统一管理,避免冲突,首屏可见)======================
|
||||
public static final String TAG = "NotificationManagerUtils";
|
||||
// 通知渠道(4大渠道,场景隔离,API26+必填)
|
||||
// 1. 前台服务保活渠道(低优先级,无打扰)
|
||||
private static final String CHANNEL_ID_FOREGROUND_SERVICE = "channel_foreground_service";
|
||||
private static final String CHANNEL_NAME_FOREGROUND_SERVICE = "前台服务保活通知";
|
||||
private static final String CHANNEL_DESC_FOREGROUND_SERVICE = "后台服务运行状态,无声音无震动,不打扰用户";
|
||||
// 2. 电量提醒渠道(高优先级,闹钟铃声+震动,强提醒)
|
||||
private static final String CHANNEL_ID_BATTERY_REMIND = "channel_battery_remind";
|
||||
private static final String CHANNEL_NAME_BATTERY_REMIND = "电量异常提醒通知";
|
||||
private static final String CHANNEL_DESC_BATTERY_REMIND = "电量过高/过低提醒,强震动+闹钟铃声,突破免打扰";
|
||||
// 3. 通用临时通知渠道(高优先级,仅震动,普通告警)
|
||||
private static final String CHANNEL_ID_TEMP_ALERT = "channel_temp_alert";
|
||||
private static final String CHANNEL_NAME_TEMP_ALERT = "通用临时提醒通知";
|
||||
private static final String CHANNEL_DESC_TEMP_ALERT = "普通即时告警,仅震动提醒,自动取消";
|
||||
// 通知ID(唯一区分,避免覆盖,按场景分段)
|
||||
public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001; // 前台服务
|
||||
public static final int NOTIFY_ID_BATTERY_REMIND = 1002; // 电量提醒
|
||||
public static final int NOTIFY_ID_TEMP_ALERT = 1003; // 通用临时通知
|
||||
public static final int NOTIFY_ID_CUSTOM_LAYOUT = 1004; // 自定义布局通知
|
||||
// 通用配置
|
||||
private static final int PENDING_INTENT_FLAGS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||
: PendingIntent.FLAG_UPDATE_CURRENT; // API30安全标志
|
||||
private static final long[] VIBRATE_PATTERN = new long[]{100, 200, 300, 400}; // 标准震动节奏
|
||||
private static final String DEFAULT_JUMP_PACKAGE = "cc.winboll.studio.powerbell"; // 默认跳转包名(API29+必填)
|
||||
|
||||
// ====================== 成员变量(按场景分组,私有封装,避免外部篡改)======================
|
||||
private final Context mContext;
|
||||
private final NotificationManager mNotificationManager;
|
||||
// 前台服务通知专属
|
||||
private Notification mForegroundServiceNotify;
|
||||
private RemoteViews mForegroundServiceRemoteViews;
|
||||
// 电量提醒通知专属
|
||||
private Notification mBatteryRemindNotify;
|
||||
private RemoteViews mBatteryRemindRemoteViews;
|
||||
|
||||
// ====================== 构造方法(单例思想/实例化通用,自动初始化渠道)======================
|
||||
public NotificationManagerUtils(Context context) {
|
||||
LogUtils.d(TAG, "【初始化】全局通知管理工具类 构造方法调用");
|
||||
this.mContext = context.getApplicationContext(); // 用应用上下文,避免内存泄漏
|
||||
this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
createAllNotificationChannels(); // 自动创建所有渠道(API26+)
|
||||
LogUtils.d(TAG, "【初始化】全局通知管理工具类 完成,渠道创建状态:" + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? "已创建4个渠道" : "无需创建"));
|
||||
}
|
||||
|
||||
// ====================== 核心基础能力(渠道创建+Intent构建,复用逻辑,减少冗余)======================
|
||||
/**
|
||||
* 创建所有通知渠道(API26+专属,低版本自动跳过,确保通知正常显示)
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void createAllNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
LogUtils.d(TAG, "【渠道管理】开始创建所有通知渠道");
|
||||
createForegroundServiceChannel();
|
||||
createBatteryRemindChannel();
|
||||
createTempAlertChannel();
|
||||
LogUtils.d(TAG, "【渠道管理】4个通知渠道创建完成(含3个核心渠道+预留扩展)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建前台服务保活渠道(IMPORTANCE_LOW,无声音无震动,不打扰用户)
|
||||
*/
|
||||
private void createForegroundServiceChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_FOREGROUND_SERVICE,
|
||||
CHANNEL_NAME_FOREGROUND_SERVICE,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription(CHANNEL_DESC_FOREGROUND_SERVICE);
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(false);
|
||||
channel.setShowBadge(false); // 不显示应用角标
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); // 锁屏隐藏
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
LogUtils.d(TAG, "【渠道管理】前台服务保活渠道创建成功:" + CHANNEL_NAME_FOREGROUND_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建电量提醒渠道(IMPORTANCE_HIGH,闹钟铃声+震动,突破免打扰)
|
||||
*/
|
||||
private void createBatteryRemindChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_BATTERY_REMIND,
|
||||
CHANNEL_NAME_BATTERY_REMIND,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
channel.setDescription(CHANNEL_DESC_BATTERY_REMIND);
|
||||
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), null); // 闹钟铃声
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(VIBRATE_PATTERN);
|
||||
channel.setBypassDnd(true); // 突破免打扰
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); // 锁屏可见
|
||||
channel.setShowBadge(true);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
LogUtils.d(TAG, "【渠道管理】电量提醒渠道创建成功:" + CHANNEL_NAME_BATTERY_REMIND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通用临时通知渠道(IMPORTANCE_HIGH,仅震动,普通告警)
|
||||
*/
|
||||
private void createTempAlertChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_TEMP_ALERT,
|
||||
CHANNEL_NAME_TEMP_ALERT,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
channel.setDescription(CHANNEL_DESC_TEMP_ALERT);
|
||||
channel.setSound(null, null); // 仅震动,不发声
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(VIBRATE_PATTERN);
|
||||
channel.setBypassDnd(false); // 不突破免打扰
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
channel.setShowBadge(true);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
LogUtils.d(TAG, "【渠道管理】通用临时通知渠道创建成功:" + CHANNEL_NAME_TEMP_ALERT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建通用跳转PendingIntent(适配API29-30安全规范,支持自定义跳转目标)
|
||||
* @param targetIntent 自定义跳转意图(传null则默认跳MainActivity)
|
||||
* @return 安全的PendingIntent,避免跳转失败/泄露
|
||||
*/
|
||||
private PendingIntent buildPendingIntent(Intent targetIntent) {
|
||||
Intent intent = targetIntent;
|
||||
// 传null则默认跳MainActivity(兼容旧逻辑)
|
||||
if (intent == null) {
|
||||
intent = new Intent(mContext, MainActivity.class);
|
||||
LogUtils.d(TAG, "【Intent构建】未传自定义Intent,默认跳转:MainActivity");
|
||||
}
|
||||
// API29+ 强制要求:明确包名,避免跳转目标模糊
|
||||
intent.setPackage(DEFAULT_JUMP_PACKAGE);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 确保跳转生效
|
||||
LogUtils.d(TAG, "【Intent构建】跳转包名:" + DEFAULT_JUMP_PACKAGE + ",目标:" + intent.getComponent().getClassName());
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
mContext,
|
||||
0,
|
||||
intent,
|
||||
PENDING_INTENT_FLAGS
|
||||
);
|
||||
LogUtils.d(TAG, "【Intent构建】PendingIntent创建成功,安全标志:" + PENDING_INTENT_FLAGS);
|
||||
return pendingIntent;
|
||||
}
|
||||
|
||||
// ====================== 场景1:前台服务保活通知(支持自定义布局+更新)======================
|
||||
/**
|
||||
* 初始化前台服务通知自定义布局(RemoteViews)
|
||||
*/
|
||||
private void initForegroundServiceRemoteViews(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【布局初始化】开始初始化前台服务通知布局,标题:" + msg.getTitle());
|
||||
mForegroundServiceRemoteViews = new RemoteViews(service.getPackageName(), R.layout.view_servicenotification);
|
||||
mForegroundServiceRemoteViews.setTextViewText(R.id.remoteviewTextView1, msg.getTitle());
|
||||
mForegroundServiceRemoteViews.setTextViewText(R.id.remoteviewTextView3, msg.getContent());
|
||||
mForegroundServiceRemoteViews.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||
LogUtils.d(TAG, "【布局初始化】前台服务通知布局填充完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动前台服务保活通知(ControlCenterService专用,API26+强制要求,保活后台服务)
|
||||
*/
|
||||
public void startForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【前台服务通知】开始构建保活通知,内容:" + msg.getContent());
|
||||
if (service == null || msg == null) {
|
||||
LogUtils.e(TAG, "【前台服务通知】构建失败:Service/NotificationMessage为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 构建跳转Intent
|
||||
PendingIntent pendingIntent = buildPendingIntent(null); // 默认跳MainActivity
|
||||
// 2. 构建基础通知(兼容API26+渠道,低版本用Builder)
|
||||
Notification.Builder builder;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder = new Notification.Builder(service, CHANNEL_ID_FOREGROUND_SERVICE);
|
||||
} else {
|
||||
builder = new Notification.Builder(service);
|
||||
}
|
||||
mForegroundServiceNotify = builder
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
|
||||
.setContentTitle(msg.getTitle())
|
||||
.setContentText(msg.getContent())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setColor(Color.parseColor("#F00606")) // 小图标背景色
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true) // 常驻通知,不可滑动取消(保活关键)
|
||||
.setAutoCancel(false) // 禁止点击取消
|
||||
.build();
|
||||
// 3. 设置自定义布局
|
||||
initForegroundServiceRemoteViews(service, msg);
|
||||
mForegroundServiceNotify.contentView = mForegroundServiceRemoteViews;
|
||||
mForegroundServiceNotify.bigContentView = mForegroundServiceRemoteViews;
|
||||
// 4. 启动前台服务(必须调用,否则Service易被回收)
|
||||
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
|
||||
LogUtils.d(TAG, "【前台服务通知】保活通知启动成功,通知ID:" + NOTIFY_ID_FOREGROUND_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前台服务保活通知内容(无需重启服务,直接刷新布局)
|
||||
*/
|
||||
public void updateForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【前台服务通知】开始更新保活通知,新内容:" + msg.getContent());
|
||||
if (mForegroundServiceNotify == null || mForegroundServiceRemoteViews == null) {
|
||||
LogUtils.e(TAG, "【前台服务通知】更新失败:通知对象未初始化,先调用startForegroundServiceNotify");
|
||||
return;
|
||||
}
|
||||
// 更新自定义布局数据
|
||||
initForegroundServiceRemoteViews(service, msg);
|
||||
mForegroundServiceNotify.contentView = mForegroundServiceRemoteViews;
|
||||
mForegroundServiceNotify.bigContentView = mForegroundServiceRemoteViews;
|
||||
// 发送更新
|
||||
mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
|
||||
LogUtils.d(TAG, "【前台服务通知】保活通知更新成功");
|
||||
}
|
||||
|
||||
// ====================== 场景2:电量提醒通知(支持自定义布局+更新+单独取消)======================
|
||||
/**
|
||||
* 初始化电量提醒通知自定义布局(RemoteViews,支持充电/耗电切换)
|
||||
*/
|
||||
private void initBatteryRemindRemoteViews(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【布局初始化】开始初始化电量提醒布局,提醒类型:" + msg.getRemindMSG());
|
||||
mBatteryRemindRemoteViews = new RemoteViews(service.getPackageName(), R.layout.view_remindnotification);
|
||||
mBatteryRemindRemoteViews.setTextViewText(R.id.viewremindnotificationTextView1, msg.getTitle());
|
||||
mBatteryRemindRemoteViews.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||
// 切换布局(+:充电提醒,-:耗电提醒)
|
||||
String remindType = msg.getRemindMSG() != null ? msg.getRemindMSG().trim() : "";
|
||||
if ("+".equals(remindType)) {
|
||||
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.GONE);
|
||||
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.VISIBLE);
|
||||
LogUtils.d(TAG, "【布局初始化】电量提醒布局切换:充电提醒");
|
||||
} else if ("-".equals(remindType)) {
|
||||
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.GONE);
|
||||
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
|
||||
LogUtils.d(TAG, "【布局初始化】电量提醒布局切换:耗电提醒");
|
||||
} else {
|
||||
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.GONE);
|
||||
mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
|
||||
LogUtils.w(TAG, "【布局初始化】未知电量提醒类型:" + remindType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化电量提醒通知(仅构建,不发送,配合update触发提醒)
|
||||
*/
|
||||
public void initBatteryRemindNotify(ControlCenterService service, NotificationMessage msg) {
|
||||
LogUtils.d(TAG, "【电量提醒通知】开始初始化提醒通知,标题:" + msg.getTitle());
|
||||
if (service == null || msg == null) {
|
||||
LogUtils.e(TAG, "【电量提醒通知】初始化失败:Service/NotificationMessage为空");
|
||||
return;
|
||||
}
|
||||
// 1. 构建跳转Intent
|
||||
PendingIntent pendingIntent = buildPendingIntent(null);
|
||||
// 2. 构建基础通知
|
||||
Notification.Builder builder;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
builder = new Notification.Builder(service, CHANNEL_ID_BATTERY_REMIND);
|
||||
} else {
|
||||
builder = new Notification.Builder(service);
|
||||
}
|
||||
mBatteryRemindNotify = builder
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
|
||||
.setContentTitle(msg.getTitle())
|
||||
.setContentText(msg.getContent())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setColor(Color.parseColor("#F00606"))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true) // 点击取消
|
||||
.build();
|
||||
// 3. 初始化自定义布局
|
||||
initBatteryRemindRemoteViews(service, msg);
|
||||
mBatteryRemindNotify.contentView = mBatteryRemindRemoteViews;
|
||||
mBatteryRemindNotify.bigContentView = mBatteryRemindRemoteViews;
|
||||
LogUtils.d(TAG, "【电量提醒通知】初始化完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送/更新电量提醒通知(初始化后调用,触发强提醒)
|
||||
*/
|
||||
public void sendOrUpdateBatteryRemindNotify() {
|
||||
LogUtils.d(TAG, "【电量提醒通知】开始发送/更新提醒");
|
||||
if (mBatteryRemindNotify == null || mBatteryRemindRemoteViews == null) {
|
||||
LogUtils.e(TAG, "【电量提醒通知】发送失败:通知未初始化,先调用initBatteryRemindNotify");
|
||||
return;
|
||||
}
|
||||
mNotificationManager.notify(NOTIFY_ID_BATTERY_REMIND, mBatteryRemindNotify);
|
||||
LogUtils.d(TAG, "【电量提醒通知】发送/更新成功,通知ID:" + NOTIFY_ID_BATTERY_REMIND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单独取消电量提醒通知(静态方法,外部可直接调用,无需实例化)
|
||||
*/
|
||||
public static void cancelBatteryRemindNotify(Context context) {
|
||||
LogUtils.d(TAG, "【电量提醒通知】开始取消提醒,通知ID:" + NOTIFY_ID_BATTERY_REMIND);
|
||||
if (context == null) {
|
||||
LogUtils.e(TAG, "【电量提醒通知】取消失败:Context为空");
|
||||
return;
|
||||
}
|
||||
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.cancel(NOTIFY_ID_BATTERY_REMIND);
|
||||
LogUtils.d(TAG, "【电量提醒通知】取消成功");
|
||||
}
|
||||
|
||||
// ====================== 场景3:通用临时通知(简单文本,自动取消,无需自定义布局)======================
|
||||
/**
|
||||
* 显示通用临时通知(普通告警,仅震动,自动取消,支持自定义跳转目标)
|
||||
* @param targetIntent 自定义跳转Intent(传null默认跳MainActivity)
|
||||
* @param title 通知标题
|
||||
* @param content 通知内容
|
||||
*/
|
||||
public void showTempAlertNotify(Intent targetIntent, String title, String content) {
|
||||
LogUtils.d(TAG, "【通用临时通知】开始构建,标题:" + title + ",内容:" + content);
|
||||
if (title == null || content == null) {
|
||||
LogUtils.e(TAG, "【通用临时通知】构建失败:标题/内容为空");
|
||||
return;
|
||||
}
|
||||
// 1. 构建跳转Intent
|
||||
PendingIntent pendingIntent = buildPendingIntent(targetIntent);
|
||||
// 2. 用NotificationCompat.Builder(兼容所有版本,简化逻辑)
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMP_ALERT)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.setVibrate(VIBRATE_PATTERN);
|
||||
// 3. 发送通知
|
||||
Notification notification = builder.build();
|
||||
mNotificationManager.notify(NOTIFY_ID_TEMP_ALERT, notification);
|
||||
LogUtils.d(TAG, "【通用临时通知】显示成功,通知ID:" + NOTIFY_ID_TEMP_ALERT);
|
||||
}
|
||||
|
||||
// ====================== 场景4:自定义布局通知(灵活扩展,支持复杂样式)======================
|
||||
/**
|
||||
* 显示自定义布局通知(支持普通布局+大布局,通用所有场景,可自定义跳转)
|
||||
* @param targetIntent 自定义跳转Intent
|
||||
* @param contentView 普通自定义布局(必填)
|
||||
* @param bigContentView 下拉大布局(可选)
|
||||
*/
|
||||
public void showCustomLayoutNotify(Intent targetIntent, RemoteViews contentView, RemoteViews bigContentView) {
|
||||
LogUtils.d(TAG, "【自定义布局通知】开始构建,布局ID:" + (contentView != null ? contentView.getLayoutId() : null));
|
||||
if (contentView == null) {
|
||||
LogUtils.e(TAG, "【自定义布局通知】构建失败:普通布局contentView为空");
|
||||
return;
|
||||
}
|
||||
// 1. 构建跳转Intent
|
||||
PendingIntent pendingIntent = buildPendingIntent(targetIntent);
|
||||
// 2. 构建自定义布局通知
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMP_ALERT)
|
||||
.setSmallIcon(R.drawable.ic_launcher) // 必传,不可省略
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContent(contentView)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true);
|
||||
// 添加大布局(可选)
|
||||
if (bigContentView != null) {
|
||||
builder.setCustomBigContentView(bigContentView);
|
||||
LogUtils.d(TAG, "【自定义布局通知】已添加下拉大布局,布局ID:" + bigContentView.getLayoutId());
|
||||
}
|
||||
// 3. 发送通知
|
||||
Notification notification = builder.build();
|
||||
mNotificationManager.notify(NOTIFY_ID_CUSTOM_LAYOUT, notification);
|
||||
LogUtils.d(TAG, "【自定义布局通知】显示成功,通知ID:" + NOTIFY_ID_CUSTOM_LAYOUT);
|
||||
}
|
||||
|
||||
// ====================== 通知取消工具(支持精准取消/全取消)======================
|
||||
/**
|
||||
* 取消指定ID的通知(精准取消,灵活控制)
|
||||
*/
|
||||
public void cancelNotifyById(int notifyId) {
|
||||
LogUtils.d(TAG, "【通知管理】开始取消通知,ID:" + notifyId);
|
||||
mNotificationManager.cancel(notifyId);
|
||||
LogUtils.d(TAG, "【通知管理】通知取消成功,ID:" + notifyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有通知(谨慎使用,会清除所有场景的通知)
|
||||
*/
|
||||
public void cancelAllNotifies() {
|
||||
LogUtils.d(TAG, "【通知管理】开始取消所有通知");
|
||||
mNotificationManager.cancelAll();
|
||||
LogUtils.d(TAG, "【通知管理】所有通知取消完成");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
/*
|
||||
* 参考:
|
||||
* https://blog.csdn.net/qq_35507234/article/details/90676587
|
||||
* https://blog.csdn.net/qq_16628781/article/details/51548324
|
||||
*/
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.media.RingtoneManager;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import cc.winboll.studio.powerbell.MainActivity;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import cc.winboll.studio.powerbell.model.NotificationMessage;
|
||||
import cc.winboll.studio.powerbell.services.ControlCenterService;
|
||||
|
||||
public class NotificationUtils2 {
|
||||
|
||||
public static final String TAG = NotificationHelper.class.getSimpleName();
|
||||
|
||||
Context mContext;
|
||||
NotificationManager mNotificationManager;
|
||||
|
||||
Notification mForegroundNotification;
|
||||
PendingIntent mForegroundPendingIntent;
|
||||
Notification mRemindNotification;
|
||||
PendingIntent mRemindPendingIntent;
|
||||
RemoteViews mrvServiceNotificationView;
|
||||
RemoteViews mrvRemindNotificationView;
|
||||
|
||||
static enum NotificationType { MIN, MAX };
|
||||
private static int _mnServiceNotificationID = 1;
|
||||
private static int _mnRemindNotificationID = 2;
|
||||
private static String _mszChannelIDService = "1";
|
||||
private static String _mszChannelNameService = "Service";
|
||||
private static String _mszChannelIDRemind = "2";
|
||||
private static String _mszChannelNameRemind = "Remind";
|
||||
|
||||
// public NotificationUtils(Context context) {
|
||||
// mContext = context;
|
||||
// mNotificationManager = (NotificationManager) context.getSystemService(
|
||||
// Context.NOTIFICATION_SERVICE);
|
||||
// }
|
||||
|
||||
public NotificationUtils2(Context context) {
|
||||
mContext = context;
|
||||
mNotificationManager = context.getSystemService(NotificationManager.class);
|
||||
//createNotificationChannels();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public void createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createServiceChannel();
|
||||
createRemindChannel();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createServiceChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
_mszChannelIDService,
|
||||
_mszChannelNameService,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription("Background service updates");
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(false);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createRemindChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
_mszChannelIDRemind,
|
||||
_mszChannelNameRemind,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
channel.setDescription("Critical reminders");
|
||||
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), null);
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(new long[]{100, 200, 300, 400});
|
||||
channel.setBypassDnd(true);
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
// 创建并发送服务通知
|
||||
//
|
||||
public void createForegroundNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
//创建Notification,传入Context和channelId
|
||||
Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
|
||||
intent.setPackage(service.getPackageName());
|
||||
//LogUtils.d(TAG, "mService.getPackageName() : " + service.getPackageName());
|
||||
intent.setClass(service, MainActivity.class);
|
||||
//LogUtils.d(TAG, "MainActivity.class.getName() : " + MainActivity.class.getName());
|
||||
//这里放一个count用来区分每一个通知
|
||||
//intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||
|
||||
//参数1:context 上下文对象
|
||||
//参数2:发送者私有的请求码(Private request code for the sender)
|
||||
//参数3:intent 意图对象
|
||||
//参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
|
||||
//mForegroundPendingIntent = PendingIntent.getActivity(mService, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mForegroundPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
mForegroundPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
mForegroundNotification = new Notification.Builder(service, _mszChannelIDService)
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle(notificationMessage.getTitle())
|
||||
.setContentText(notificationMessage.getContent())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
//设置红色
|
||||
.setColor(Color.parseColor("#F00606"))
|
||||
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
|
||||
.setContentIntent(mForegroundPendingIntent)
|
||||
.build();
|
||||
|
||||
setForegroundNotificationRemoteViews(service, notificationMessage);
|
||||
service.startForeground(_mnServiceNotificationID, mForegroundNotification);
|
||||
}
|
||||
|
||||
void initmrvRemindNotificationView(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
mrvRemindNotificationView = new RemoteViews(service.getPackageName(), R.layout.view_remindnotification);
|
||||
mrvRemindNotificationView.setTextViewText(R.id.viewremindnotificationTextView1, notificationMessage.getTitle());
|
||||
String szRemindMSG = notificationMessage.getRemindMSG();
|
||||
//LogUtils.d(TAG, "szRemindMSG : " + szRemindMSG);
|
||||
//mrvRemindNotificationView.setTextViewText(R.id.remoteviewTextView2, szRemindMSG);
|
||||
if (szRemindMSG != null) {
|
||||
if (szRemindMSG.trim().equals("-")) {
|
||||
//LogUtils.d(TAG, "-");
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewCharge, View.GONE);
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
|
||||
} else if (szRemindMSG.trim().equals("+")) {
|
||||
//LogUtils.d(TAG, "+");
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewUsege, View.GONE);
|
||||
mrvRemindNotificationView.setViewVisibility(R.id.remoteviewCharge, View.VISIBLE);
|
||||
}
|
||||
mrvRemindNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||
//给我remoteViews上的控件tv_content添加监听事件
|
||||
//remoteViews.setOnClickPendingIntent(R.id.remoteviewLinearLayout1, pi);
|
||||
//return mrvServiceNotificationView;
|
||||
}
|
||||
}
|
||||
|
||||
void initmrvServiceNotificationView(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
mrvServiceNotificationView = new RemoteViews(service.getPackageName(), R.layout.view_servicenotification);
|
||||
mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView1, notificationMessage.getTitle());
|
||||
//String szRemindMSG = notificationMessage.getRemindMSG();
|
||||
//mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView2, szRemindMSG);
|
||||
//rvServiceNotificationView.setTextViewText(R.id.remoteviewTextView3, notificationMessage.getContent() + Integer.toString(nTest));
|
||||
mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView3, notificationMessage.getContent());
|
||||
mrvServiceNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||
//给我remoteViews上的控件tv_content添加监听事件
|
||||
//remoteViews.setOnClickPendingIntent(R.id.remoteviewLinearLayout1, pi);
|
||||
//return mrvServiceNotificationView;
|
||||
}
|
||||
|
||||
void setForegroundNotificationRemoteViews(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
initmrvServiceNotificationView(service, notificationMessage);
|
||||
mForegroundNotification.contentView = mrvServiceNotificationView;
|
||||
mForegroundNotification.bigContentView = mrvServiceNotificationView;
|
||||
}
|
||||
|
||||
void setRemindNotificationRemoteViews(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
initmrvRemindNotificationView(service, notificationMessage);
|
||||
mRemindNotification.contentView = mrvRemindNotificationView;
|
||||
mRemindNotification.bigContentView = mrvRemindNotificationView;
|
||||
}
|
||||
|
||||
// 更新服务通知
|
||||
//
|
||||
public void updateForegroundNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
setForegroundNotificationRemoteViews(service, notificationMessage);
|
||||
mNotificationManager.notify(_mnServiceNotificationID, mForegroundNotification);
|
||||
|
||||
}
|
||||
|
||||
// 创建并发送电量提醒通知
|
||||
//
|
||||
public void updateRemindNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
//LogUtils.d(TAG, "updateRemindNotification : " + notificationMessage.getRemindMSG());
|
||||
setRemindNotificationRemoteViews(service, notificationMessage);
|
||||
mNotificationManager.notify(_mnRemindNotificationID, mRemindNotification);
|
||||
}
|
||||
|
||||
public void createRemindNotification(ControlCenterService service, NotificationMessage notificationMessage) {
|
||||
//LogUtils.d(TAG, "notificationMessage : " + notificationMessage.getRemindMSG());
|
||||
//创建Notification,传入Context和channelId
|
||||
Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
|
||||
intent.setPackage(service.getPackageName());
|
||||
intent.setClass(service, MainActivity.class);
|
||||
//这里放一个count用来区分每一个通知
|
||||
//intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||
|
||||
//参数1:context 上下文对象
|
||||
//参数2:发送者私有的请求码(Private request code for the sender)
|
||||
//参数3:intent 意图对象
|
||||
//参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
|
||||
//mRemindPendingIntent = PendingIntent.getActivity(mService, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mRemindPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
mRemindPendingIntent = PendingIntent.getActivity(service,
|
||||
1, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
mRemindNotification = new Notification.Builder(service, _mszChannelIDRemind)
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle(notificationMessage.getTitle())
|
||||
.setContentText(notificationMessage.getContent())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
//设置红色
|
||||
.setColor(Color.parseColor("#F00606"))
|
||||
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
|
||||
.setContentIntent(mRemindPendingIntent)
|
||||
.build();
|
||||
setRemindNotificationRemoteViews(service, notificationMessage);
|
||||
}
|
||||
|
||||
public static void cancelRemindNotification(Context context){
|
||||
// 获取 NotificationManager 实例
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
// 撤回指定 ID 的通知栏消息
|
||||
notificationManager.cancel(_mnRemindNotificationID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,214 +1,134 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/12/01 16:05
|
||||
* @Describe 权限申请工具类(单例)
|
||||
* 核心特性:
|
||||
* 1. 适配全Android版本(6.0+ 动态权限 / 13+ 兼容)
|
||||
* 2. 支持多包名场景(无硬编码包名)
|
||||
* 3. 统一权限校验、申请、回调处理
|
||||
* 4. 自带用户引导(拒绝权限+不再询问场景)
|
||||
* 权限申请工具类
|
||||
* 适配 Android 13+ 媒体权限 & 低版本存储权限
|
||||
* 兼容 SDK 版本低于 33 的编译环境
|
||||
*/
|
||||
public class PermissionUtils {
|
||||
public static final String TAG = "PermissionUtils";
|
||||
// 存储权限请求码(与Activity保持一致,避免冲突)
|
||||
public static final int STORAGE_PERMISSION_REQUEST2 = 100;
|
||||
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 101;
|
||||
private static final String TAG = "PermissionUtils";
|
||||
// 存储权限请求码
|
||||
public static final int REQUEST_STORAGE = 1000;
|
||||
// 媒体图片权限请求码(Android 13+)
|
||||
public static final int REQUEST_READ_MEDIA_IMAGES = 1001;
|
||||
|
||||
// 单例实例(双重校验锁,线程安全)
|
||||
private static volatile PermissionUtils sInstance;
|
||||
// 手动定义 Android 13+ 媒体图片权限常量(解决 SDK 低于 33 无法识别问题)
|
||||
private static final String READ_MEDIA_IMAGES = "android.permission.READ_MEDIA_IMAGES";
|
||||
// Android 13 对应的 SDK 版本号(替代 Build.VERSION_CODES.TIRAMISU)
|
||||
private static final int SDK_VERSION_TIRAMISU = 33;
|
||||
|
||||
// 私有构造(禁止外部实例化)
|
||||
private PermissionUtils() {}
|
||||
// 单例模式
|
||||
private static volatile PermissionUtils sInstance;
|
||||
|
||||
/**
|
||||
* 获取单例实例(适配多包名,无硬编码)
|
||||
*/
|
||||
public static PermissionUtils getInstance() {
|
||||
if (sInstance == null) {
|
||||
synchronized (PermissionUtils.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new PermissionUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
private PermissionUtils() {}
|
||||
|
||||
// ======================================== 存储权限核心方法 ========================================
|
||||
/**
|
||||
* 检查并申请存储权限(统一入口,适配全Android版本)
|
||||
* @param activity 上下文(用于权限申请和弹窗)
|
||||
* @return true:权限已全部获取;false:需要申请权限
|
||||
*/
|
||||
public boolean checkAndRequestStoragePermission(Activity activity) {
|
||||
if (activity == null || activity.isFinishing()) {
|
||||
LogUtils.e(TAG, "【权限检查】Activity为空或已销毁,权限检查失败");
|
||||
return false;
|
||||
}
|
||||
LogUtils.d(TAG, "【权限检查】开始检查存储权限,Android版本:" + Build.VERSION.SDK_INT);
|
||||
public static PermissionUtils getInstance() {
|
||||
if (sInstance == null) {
|
||||
synchronized (PermissionUtils.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new PermissionUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
// 统一使用 WRITE_EXTERNAL_STORAGE + READ_EXTERNAL_STORAGE(适配所有版本,避免READ_MEDIA_IMAGES找不到符号)
|
||||
String[] requiredPermissions = {
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
};
|
||||
/**
|
||||
* 检查并请求 存储权限(Android 12及以下)
|
||||
*/
|
||||
public boolean checkAndRequestStoragePermission(Activity activity) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Android 11+ 无需申请 READ_EXTERNAL_STORAGE,直接返回true
|
||||
return true;
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
String[] permissions = {
|
||||
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
};
|
||||
if (ContextCompat.checkSelfPermission(activity, permissions[0]) != PackageManager.PERMISSION_GRANTED
|
||||
|| ContextCompat.checkSelfPermission(activity, permissions[1]) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(activity, permissions, REQUEST_STORAGE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 筛选未授予的权限
|
||||
List<String> needPermissions = new ArrayList<>();
|
||||
for (String permission : requiredPermissions) {
|
||||
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
needPermissions.add(permission);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查并请求 媒体图片权限(Android 13+)
|
||||
* 兼容 SDK 编译版本低于 33 的情况
|
||||
*/
|
||||
public boolean checkAndRequestMediaImagesPermission(Activity activity, int requestCode) {
|
||||
// 用数值 33 替代 Build.VERSION_CODES.TIRAMISU
|
||||
if (Build.VERSION.SDK_INT >= SDK_VERSION_TIRAMISU) {
|
||||
// 用手动定义的权限常量替代 android.Manifest.permission.READ_MEDIA_IMAGES
|
||||
if (ContextCompat.checkSelfPermission(activity, READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(activity, new String[]{READ_MEDIA_IMAGES}, requestCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 低版本已通过存储权限覆盖,直接返回true
|
||||
return true;
|
||||
}
|
||||
|
||||
// 无权限需要申请:触发动态申请
|
||||
if (!needPermissions.isEmpty()) {
|
||||
String[] permissionsArr = needPermissions.toArray(new String[0]);
|
||||
ActivityCompat.requestPermissions(activity, permissionsArr, STORAGE_PERMISSION_REQUEST2);
|
||||
LogUtils.d(TAG, "【权限申请】已触发存储权限申请:" + TextUtils.join(",", permissionsArr));
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 权限请求结果处理
|
||||
*/
|
||||
public void handleStoragePermissionResult(Activity activity, int requestCode, String[] permissions, int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_STORAGE:
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
LogUtils.d(TAG, "存储权限申请成功");
|
||||
} else {
|
||||
LogUtils.e(TAG, "存储权限申请失败");
|
||||
}
|
||||
break;
|
||||
case REQUEST_READ_MEDIA_IMAGES:
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
LogUtils.d(TAG, "媒体图片权限申请成功");
|
||||
} else {
|
||||
LogUtils.e(TAG, "媒体图片权限申请失败");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogUtils.d(TAG, "未知权限请求码:" + requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
// 所有权限已授予
|
||||
LogUtils.d(TAG, "【权限检查】存储权限已全部获取");
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 检查是否有管理所有文件权限(Android 11+)
|
||||
*/
|
||||
public boolean checkManageExternalStoragePermission(Activity activity) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
return android.os.Environment.isExternalStorageManager();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理存储权限申请回调(统一逻辑,无需在Activity中重复编写)
|
||||
* @param activity 上下文
|
||||
* @param requestCode 请求码(匹配STORAGE_PERMISSION_REQUEST)
|
||||
* @param permissions 申请的权限数组
|
||||
* @param grantResults 权限授予结果数组
|
||||
* @return true:回调已处理;false:非当前工具类的权限回调
|
||||
*/
|
||||
public boolean handleStoragePermissionResult(Activity activity, int requestCode, String[] permissions, int[] grantResults) {
|
||||
// 过滤非存储权限回调
|
||||
if (requestCode != STORAGE_PERMISSION_REQUEST2) {
|
||||
return false;
|
||||
}
|
||||
LogUtils.d(TAG, "【权限回调】处理存储权限回调,requestCode:" + requestCode);
|
||||
|
||||
if (activity == null || activity.isFinishing()) {
|
||||
LogUtils.e(TAG, "【权限回调】Activity为空或已销毁,回调处理终止");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 校验所有权限是否授予
|
||||
boolean allGranted = true;
|
||||
for (int grantResult : grantResults) {
|
||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
allGranted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allGranted) {
|
||||
// 全部授予:提示用户重新操作
|
||||
ToastUtils.show(activity.getString(R.string.permission_grant_success));
|
||||
LogUtils.d(TAG, "【权限回调】所有存储权限已授予");
|
||||
} else {
|
||||
// 部分/全部拒绝:判断是否勾选“不再询问”
|
||||
boolean shouldShowRationale = false;
|
||||
for (String permission : permissions) {
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
|
||||
shouldShowRationale = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldShowRationale) {
|
||||
// 未勾选“不再询问”:弹窗引导重新申请
|
||||
showPermissionRationaleDialog(activity);
|
||||
} else {
|
||||
// 已勾选“不再询问”:引导用户去设置页开启
|
||||
showPermissionSettingDialog(activity);
|
||||
}
|
||||
LogUtils.d(TAG, "【权限回调】部分/全部存储权限被拒绝,是否需要引导:" + shouldShowRationale);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ======================================== 辅助方法(私有,封装细节) ========================================
|
||||
/**
|
||||
* 弹窗:未勾选“不再询问”时,提示用户授予权限
|
||||
*/
|
||||
private void showPermissionRationaleDialog(final Activity activity) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.permission_title))
|
||||
.setMessage(activity.getString(R.string.permission_storage_rationale))
|
||||
.setPositiveButton(activity.getString(R.string.confirm), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// 重新申请权限
|
||||
checkAndRequestStoragePermission(activity);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(activity.getString(R.string.cancel), null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹窗:已勾选“不再询问”时,引导用户去应用设置页开启权限
|
||||
*/
|
||||
private void showPermissionSettingDialog(final Activity activity) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.permission_denied_title))
|
||||
.setMessage(activity.getString(R.string.permission_storage_setting_guide))
|
||||
.setPositiveButton(activity.getString(R.string.go_to_setting), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// 跳转应用设置页(适配多包名,动态获取当前包名)
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(activity.getString(R.string.cancel), null)
|
||||
.show();
|
||||
}
|
||||
|
||||
// ======================================== 扩展:其他权限方法(可选) ========================================
|
||||
/**
|
||||
* 检查单个权限是否已授予(通用方法,可复用)
|
||||
*/
|
||||
public boolean isPermissionGranted(Activity activity, String permission) {
|
||||
if (activity == null || TextUtils.isEmpty(permission)) {
|
||||
return false;
|
||||
}
|
||||
return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请单个权限(通用方法,可复用)
|
||||
*/
|
||||
public void requestSinglePermission(Activity activity, String permission, int requestCode) {
|
||||
if (activity == null || TextUtils.isEmpty(permission)) {
|
||||
return;
|
||||
}
|
||||
if (!isPermissionGranted(activity, permission)) {
|
||||
ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 请求管理所有文件权限(Android 11+)
|
||||
*/
|
||||
public void requestManageExternalStoragePermission(Activity activity, int requestCode) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
try {
|
||||
android.content.Intent intent = new android.content.Intent(
|
||||
android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
|
||||
);
|
||||
intent.setData(android.net.Uri.parse("package:" + activity.getPackageName()));
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "请求管理文件权限异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import cc.winboll.studio.powerbell.model.BatteryInfoBean;
|
||||
import cc.winboll.studio.powerbell.models.BatteryInfoBean;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
@@ -10,15 +10,17 @@ import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.core.content.FileProvider;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class UriUtil {
|
||||
public class UriUtils {
|
||||
|
||||
public static final String TAG = "UriUtil";
|
||||
|
||||
@@ -105,18 +107,83 @@ public class UriUtil {
|
||||
}
|
||||
|
||||
public static Uri getUriForFile(Context context, String filePath) {
|
||||
// 1. 打印传入的文件路径
|
||||
LogUtils.d(TAG, "getUriForFile -> 传入路径:" + filePath);
|
||||
if (filePath == null || filePath.isEmpty()) {
|
||||
LogUtils.e(TAG, "getUriForFile -> 传入路径为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = new File(filePath);
|
||||
return getUriForFile(context, file);
|
||||
}
|
||||
// 2. 打印File对象的绝对路径和存在性
|
||||
LogUtils.d(TAG, "getUriForFile -> 文件绝对路径:" + file.getAbsolutePath());
|
||||
LogUtils.d(TAG, "getUriForFile -> 文件是否存在:" + file.exists());
|
||||
LogUtils.d(TAG, "getUriForFile -> 是否为目录:" + file.isDirectory());
|
||||
|
||||
public static Uri getUriForFile(Context context, File file) {
|
||||
//Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
|
||||
if (Build.VERSION.SDK_INT >= 24) {//android 7.0以上
|
||||
return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
|
||||
}
|
||||
return Uri.fromFile(file);
|
||||
}
|
||||
// 3. 合法性校验
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
LogUtils.e(TAG, "getUriForFile -> 非法路径:文件不存在或为目录");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 4. 校验路径是否在配置的合法目录内
|
||||
String appFilesDir = context.getExternalFilesDir(null) != null ? context.getExternalFilesDir(null).getAbsolutePath() : "null";
|
||||
String publicPicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/PowerBell/";
|
||||
String internalFilesDir = context.getFilesDir().getAbsolutePath();
|
||||
String cacheDir = context.getCacheDir().getAbsolutePath();
|
||||
|
||||
String absolutePath = file.getAbsolutePath();
|
||||
boolean isInConfigDir = absolutePath.startsWith(appFilesDir)
|
||||
|| absolutePath.startsWith(publicPicDir)
|
||||
|| absolutePath.startsWith(internalFilesDir)
|
||||
|| absolutePath.startsWith(cacheDir);
|
||||
LogUtils.d(TAG, "getUriForFile -> 路径是否在配置目录内:" + isInConfigDir);
|
||||
if (!isInConfigDir) {
|
||||
LogUtils.w(TAG, "getUriForFile -> 路径不在FileProvider配置范围内,可能导致异常");
|
||||
// 非强制拦截,保留原有逻辑,仅警告
|
||||
}
|
||||
|
||||
return getUriForFile(context, file);
|
||||
}
|
||||
|
||||
public static Uri getUriForFile(Context context, File file) {
|
||||
if (context == null) {
|
||||
LogUtils.e(TAG, "getUriForFile -> Context为空");
|
||||
return null;
|
||||
}
|
||||
if (file == null) {
|
||||
LogUtils.e(TAG, "getUriForFile -> File对象为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1. 二次校验文件状态
|
||||
LogUtils.d(TAG, "getUriForFile(File) -> 文件路径:" + file.getAbsolutePath());
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
LogUtils.e(TAG, "getUriForFile(File) -> 文件不存在或为目录");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 版本判断与Uri生成
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
LogUtils.d(TAG, "getUriForFile -> Android 7.0+,使用FileProvider生成Uri");
|
||||
try {
|
||||
String authority = context.getPackageName() + ".fileprovider";
|
||||
LogUtils.d(TAG, "getUriForFile -> FileProvider authority:" + authority);
|
||||
Uri uri = FileProvider.getUriForFile(context, authority, file);
|
||||
LogUtils.d(TAG, "getUriForFile -> 生成Content Uri成功:" + uri.toString());
|
||||
return uri;
|
||||
} catch (IllegalArgumentException e) {
|
||||
LogUtils.e(TAG, "getUriForFile -> FileProvider生成Uri失败:路径未配置或权限不足", e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
LogUtils.d(TAG, "getUriForFile -> Android 7.0以下,使用Uri.fromFile生成Uri");
|
||||
Uri uri = Uri.fromFile(file);
|
||||
LogUtils.d(TAG, "getUriForFile -> 生成File Uri成功:" + uri.toString());
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
private static File createTemporalFileFrom(Context context, InputStream inputStream, String fileName)
|
||||
throws IOException {
|
||||
File targetFile = null;
|
||||
@@ -12,7 +12,9 @@ import android.widget.ImageView.ScaleType;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.models.BackgroundBean;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
@@ -22,6 +24,8 @@ import java.io.File;
|
||||
public class BackgroundView extends RelativeLayout {
|
||||
|
||||
public static final String TAG = "BackgroundView";
|
||||
// 新增:记录当前已缓存的图片路径
|
||||
private String mCurrentCachedPath = "";
|
||||
|
||||
private Context mContext;
|
||||
private LinearLayout mLlContainer; // 主容器LinearLayout
|
||||
@@ -55,8 +59,6 @@ public class BackgroundView extends RelativeLayout {
|
||||
LogUtils.d(TAG, "=== initView 启动 ===");
|
||||
// 1. 配置当前控件:全屏+透明
|
||||
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
//setBackgroundColor(0x00000000);
|
||||
//setBackground(new ColorDrawable(0x00000000));
|
||||
|
||||
// 2. 初始化主容器LinearLayout
|
||||
initLinearLayout();
|
||||
@@ -64,7 +66,7 @@ public class BackgroundView extends RelativeLayout {
|
||||
// 3. 初始化ImageView
|
||||
initImageView();
|
||||
|
||||
// 初始设置透明背景
|
||||
// 初始设置透明背景
|
||||
setDefaultTransparentBackground();
|
||||
|
||||
LogUtils.d(TAG, "=== initView 完成 ===");
|
||||
@@ -101,22 +103,35 @@ public class BackgroundView extends RelativeLayout {
|
||||
LogUtils.d(TAG, "=== initImageView 完成 ===");
|
||||
}
|
||||
|
||||
public void loadBackgroundBean(BackgroundBean bean) {
|
||||
if (!bean.isUseBackgroundFile()) {
|
||||
setDefaultTransparentBackground();
|
||||
public void loadBackgroundBean(BackgroundBean bean) {
|
||||
loadBackgroundBean(bean, false);
|
||||
}
|
||||
|
||||
public void loadBackgroundBean(BackgroundBean bean, boolean isRefresh) {
|
||||
if (!bean.isUseBackgroundFile()) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
String targetPath = bean.isUseBackgroundScaledCompressFile()
|
||||
? bean.getBackgroundScaledCompressFilePath()
|
||||
: bean.getBackgroundFilePath();
|
||||
|
||||
if (!(new File(targetPath).exists())) {
|
||||
LogUtils.d(TAG, String.format("视图控件图片不存在:%s", targetPath));
|
||||
return;
|
||||
}
|
||||
if (bean.isUseBackgroundScaledCompressFile()) {
|
||||
loadImage(bean.getBackgroundScaledCompressFilePath());
|
||||
} else {
|
||||
|
||||
loadImage(bean.getBackgroundFilePath());
|
||||
// 调用带路径判断的loadImage方法
|
||||
if (isRefresh) {
|
||||
App._mBitmapCacheUtils.removeCachedBitmap(targetPath);
|
||||
App._mBitmapCacheUtils.cacheBitmap(targetPath);
|
||||
}
|
||||
}
|
||||
loadImage(targetPath);
|
||||
}
|
||||
|
||||
// ====================================== 对外方法 ======================================
|
||||
/**
|
||||
* 加载图片(保持原图比例,在LinearLayout中居中平铺)
|
||||
* 改造后:添加路径判断,路径更新时同步更新缓存;缓存Bitmap为null时提示并加载透明背景
|
||||
* @param imagePath 图片绝对路径
|
||||
*/
|
||||
public void loadImage(String imagePath) {
|
||||
@@ -132,25 +147,59 @@ public class BackgroundView extends RelativeLayout {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
mIvBackground.setVisibility(View.GONE);
|
||||
|
||||
// 计算原图比例
|
||||
mIvBackground.setVisibility(View.GONE);
|
||||
|
||||
// ======================== 新增:路径判断逻辑 ========================
|
||||
// 1. 路径未变化:直接使用缓存
|
||||
if (imagePath.equals(mCurrentCachedPath)) {
|
||||
Bitmap cachedBitmap = App._mBitmapCacheUtils.getCachedBitmap(imagePath);
|
||||
// 核心修改:判断缓存Bitmap是否为null
|
||||
if (cachedBitmap != null && !cachedBitmap.isRecycled()) {
|
||||
LogUtils.d(TAG, "loadImage: 路径未变,使用缓存 Bitmap");
|
||||
mImageAspectRatio = (float) cachedBitmap.getWidth() / cachedBitmap.getHeight();
|
||||
mIvBackground.setImageBitmap(cachedBitmap);
|
||||
adjustImageViewSize();
|
||||
mIvBackground.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
} else {
|
||||
// 缓存Bitmap为空或已回收,提示并加载透明背景
|
||||
LogUtils.e(TAG, "loadImage: 全局位图缓存为空或已回收 - " + imagePath);
|
||||
ToastUtils.show("全局位图缓存为空,无法加载图片");
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 路径已更新:移除旧缓存,加载新图片并更新缓存
|
||||
if (!TextUtils.isEmpty(mCurrentCachedPath)) {
|
||||
App._mBitmapCacheUtils.removeCachedBitmap(mCurrentCachedPath);
|
||||
LogUtils.d(TAG, "loadImage: 路径已更新,移除旧缓存 - " + mCurrentCachedPath);
|
||||
}
|
||||
// ======================== 路径判断逻辑结束 ========================
|
||||
|
||||
// 无缓存/路径更新:走原有逻辑加载图片
|
||||
if (!calculateImageAspectRatio(imageFile)) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩加载Bitmap
|
||||
Bitmap bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920);
|
||||
if (bitmap == null) {
|
||||
LogUtils.e(TAG, "loadImage: 图片解码失败");
|
||||
ToastUtils.show("图片解码失败,无法加载");
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置图片
|
||||
// 缓存新图片,并更新当前缓存路径记录
|
||||
App._mBitmapCacheUtils.cacheBitmap(imagePath);
|
||||
mCurrentCachedPath = imagePath;
|
||||
LogUtils.d(TAG, "loadImage: 加载新图片并更新缓存 - " + imagePath);
|
||||
|
||||
mIvBackground.setImageDrawable(new BitmapDrawable(mContext.getResources(), bitmap));
|
||||
adjustImageViewSize(); // 调整尺寸
|
||||
adjustImageViewSize();
|
||||
mIvBackground.setVisibility(View.VISIBLE);
|
||||
LogUtils.d(TAG, "=== loadImage 完成 ===");
|
||||
}
|
||||
|
||||
@@ -198,56 +247,40 @@ public class BackgroundView extends RelativeLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整ImageView尺寸(保持原图比例,在LinearLayout中居中平铺)
|
||||
*/
|
||||
private void adjustImageViewSize() {
|
||||
//LogUtils.d(TAG, "=== adjustImageViewSize 启动 ===");
|
||||
if (mLlContainer == null || mIvBackground == null) {
|
||||
//LogUtils.e(TAG, "控件为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取LinearLayout尺寸
|
||||
int llWidth = mLlContainer.getWidth();
|
||||
int llHeight = mLlContainer.getHeight();
|
||||
if (llWidth == 0 || llHeight == 0) {
|
||||
postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adjustImageViewSize();
|
||||
}
|
||||
}, 150);
|
||||
return;
|
||||
|
||||
if (llWidth != 0 && llHeight != 0) {
|
||||
int ivWidth, ivHeight;
|
||||
if (mImageAspectRatio >= 1.0f) {
|
||||
ivWidth = Math.min((int) (llHeight * mImageAspectRatio), llWidth);
|
||||
ivHeight = (int) (ivWidth / mImageAspectRatio);
|
||||
} else {
|
||||
ivHeight = Math.min((int) (llWidth / mImageAspectRatio), llHeight);
|
||||
ivWidth = (int) (ivHeight * mImageAspectRatio);
|
||||
}
|
||||
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mIvBackground.getLayoutParams();
|
||||
params.width = ivWidth;
|
||||
params.height = ivHeight;
|
||||
mIvBackground.setLayoutParams(params);
|
||||
mIvBackground.setScaleType(ScaleType.FIT_CENTER);
|
||||
mIvBackground.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// 计算ImageView尺寸(保持比例,不超出LinearLayout)
|
||||
int ivWidth, ivHeight;
|
||||
if (mImageAspectRatio >= 1.0f) {
|
||||
ivWidth = Math.min((int) (llHeight * mImageAspectRatio), llWidth);
|
||||
ivHeight = (int) (ivWidth / mImageAspectRatio);
|
||||
} else {
|
||||
ivHeight = Math.min((int) (llWidth / mImageAspectRatio), llHeight);
|
||||
ivWidth = (int) (ivHeight * mImageAspectRatio);
|
||||
}
|
||||
|
||||
// 应用尺寸
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mIvBackground.getLayoutParams();
|
||||
params.width = ivWidth;
|
||||
params.height = ivHeight;
|
||||
mIvBackground.setLayoutParams(params);
|
||||
mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 确保居中平铺
|
||||
mIvBackground.setVisibility(View.VISIBLE);
|
||||
|
||||
|
||||
//LogUtils.d(TAG, "ImageView尺寸:" + ivWidth + "x" + ivHeight);
|
||||
//LogUtils.d(TAG, "=== adjustImageViewSize 完成 ===");
|
||||
}
|
||||
|
||||
private void setDefaultTransparentBackground() {
|
||||
mIvBackground.setImageBitmap(null);
|
||||
mIvBackground.setBackgroundColor(0x00000000);
|
||||
mImageAspectRatio = 1.0f;
|
||||
// 清空缓存路径记录
|
||||
mCurrentCachedPath = "";
|
||||
mIvBackground.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// ====================================== 重写方法 ======================================
|
||||
@@ -257,3 +290,4 @@ public class BackgroundView extends RelativeLayout {
|
||||
adjustImageViewSize(); // 尺寸变化时重新调整
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,251 +1,259 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<cc.winboll.studio.libaes.views.ASupportToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/toolbar_height"
|
||||
android:id="@+id/toolbar"
|
||||
android:gravity="center_vertical"
|
||||
style="@style/DefaultAToolbar"/>
|
||||
<!-- 顶部Toolbar(首屏核心,同步加载,保留原有ASupportToolbar) -->
|
||||
<cc.winboll.studio.libaes.views.ASupportToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/toolbar_height"
|
||||
android:id="@+id/toolbar"
|
||||
android:gravity="center_vertical"
|
||||
style="@style/DefaultAToolbar"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
<!-- 主内容区(优化层级,减少冗余RelativeLayout) -->
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/activitymainRelativeLayout1"/>
|
||||
<!-- 首屏核心容器(合并原冗余RelativeLayout,减少层级) -->
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/activitymainRelativeLayout1">
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<!-- 1. 背景视图(首屏核心,同步加载,保留原有) -->
|
||||
<cc.winboll.studio.powerbell.views.BackgroundView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/fragmentmainviewBackgroundView1"/>
|
||||
|
||||
<cc.winboll.studio.powerbell.views.BackgroundView
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/fragmentmainviewBackgroundView1"/>
|
||||
<!-- 2. 功能控件容器(首屏核心,同步加载,保留原有结构) -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<!-- 服务总开关布局 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentmainviewLinearLayout3"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentmainviewLinearLayout3"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="10dp">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/bg_frame">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/bg_frame">
|
||||
<Switch
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewSwitch1"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="1.0"
|
||||
android:textSize="@dimen/text_title_size"/>
|
||||
|
||||
<Switch
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewSwitch1"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="1.0"
|
||||
android:textSize="@dimen/text_title_size"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- 电量控制核心布局(SeekBar+图标) -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.0">
|
||||
<!-- 耗电提醒布局 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:id="@+id/fragmentmainviewLinearLayout1">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:id="@+id/fragmentmainviewLinearLayout1">
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/usege"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/usege"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"/>
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:id="@+id/fragmentmainviewCheckBox2"/>
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:id="@+id/fragmentmainviewCheckBox2"/>
|
||||
<cc.winboll.studio.powerbell.views.VerticalSeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewVerticalSeekBar2"
|
||||
android:progressTint="@color/colorUsege"
|
||||
android:progressBackgroundTint="@color/colorUsege"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_margin="10dp"/>
|
||||
|
||||
<cc.winboll.studio.powerbell.views.VerticalSeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewVerticalSeekBar2"
|
||||
android:progressTint="@color/colorUsege"
|
||||
android:progressBackgroundTint="@color/colorUsege"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_margin="10dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- 耗电提醒数值+图标 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView3"
|
||||
android:gravity="center_horizontal"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView3"
|
||||
android:gravity="center_horizontal"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView2"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView2"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- 当前电量数值+图标 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.0">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView4"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView4"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView1"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView1"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- 充电提醒数值+图标 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView2"
|
||||
android:gravity="center_horizontal"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textSize="@dimen/text_title_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/fragmentandroidviewTextView2"
|
||||
android:gravity="center_horizontal"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView3"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewImageView3"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- 充电提醒布局 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:id="@+id/fragmentmainviewLinearLayout2">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:id="@+id/fragmentmainviewLinearLayout2">
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/charge"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/charge"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"/>
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:id="@+id/fragmentmainviewCheckBox1"/>
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:id="@+id/fragmentmainviewCheckBox1"/>
|
||||
<cc.winboll.studio.powerbell.views.VerticalSeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewVerticalSeekBar1"
|
||||
android:progressTint="@color/colorCharge"
|
||||
android:progressBackgroundTint="@color/colorCharge"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_margin="10dp"/>
|
||||
|
||||
<cc.winboll.studio.powerbell.views.VerticalSeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/fragmentandroidviewVerticalSeekBar1"
|
||||
android:progressTint="@color/colorCharge"
|
||||
android:progressBackgroundTint="@color/colorCharge"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_margin="10dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- Tips文本 -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tips"
|
||||
android:textSize="@dimen/text_content_size"
|
||||
android:id="@+id/fragmentandroidviewTextView1"
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tips"
|
||||
android:textSize="@dimen/text_content_size"
|
||||
android:id="@+id/fragmentandroidviewTextView1"
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<!-- 3. 广告视图:关键优化→用ViewStub延迟加载(替代原直接加载的ADsBannerView) -->
|
||||
<!-- 首次启动仅占位(1px),不inflate真实广告视图,减少首次耗时 -->
|
||||
<ViewStub
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/stub_ads_banner"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout="@layout/view_ads_banner"/> <!-- 广告视图独立布局文件 -->
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<cc.winboll.studio.libaes.views.ADsBannerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/adsbanner"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical|center_horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"
|
||||
android:id="@+id/dialogbackgroundpicturepreviewImageView1"/>
|
||||
<cc.winboll.studio.powerbell.views.BackgroundView
|
||||
android:orientation="vertical"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:id="@+id/backgroundview"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
7
powerbell/src/main/res/layout/view_ads_banner.xml
Normal file
7
powerbell/src/main/res/layout/view_ads_banner.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 广告视图独立布局(供ViewStub延迟加载) -->
|
||||
<cc.winboll.studio.libaes.views.ADsBannerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/adsbanner"/>
|
||||
@@ -2,9 +2,6 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_log"
|
||||
android:title="@string/item_logview"/>
|
||||
<item
|
||||
android:id="@+id/action_unittestactivity"
|
||||
android:title="@string/item_mainunittestactivity"/>
|
||||
|
||||
Reference in New Issue
Block a user