From eff1822ee56fddb428f77e22857925f027b12943 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Tue, 23 Dec 2025 13:43:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BA=90=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../BackgroundSettingsActivity.java | 2 +- .../activities/PixelPickerActivity.java | 441 +++++++++++------- .../activities/SettingsActivity.java | 49 +- .../activities/ShortcutActionActivity.java | 72 ++- .../powerbell/activities/WinBoLLActivity.java | 172 +++++-- .../powerbell/adapters/BatteryAdapter.java | 84 +++- .../BackgroundPicturePreviewDialog.java | 229 ++++----- .../powerbell/dialogs/ColorPaletteDialog.java | 426 ++++++++--------- .../dialogs/NetworkBackgroundDialog.java | 237 ++++++---- .../powerbell/dialogs/YesNoAlertDialog.java | 59 --- .../powerbell/models/AppConfigBean.java | 169 ++++--- .../powerbell/models/BackgroundBean.java | 139 ++++-- .../studio/powerbell/models/BatteryData.java | 79 +++- .../powerbell/models/BatteryInfoBean.java | 102 +++- .../studio/powerbell/utils/AppCacheUtils.java | 4 +- .../studio/powerbell/utils/StringUtils.java | 4 +- 17 files changed, 1366 insertions(+), 906 deletions(-) delete mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/YesNoAlertDialog.java diff --git a/powerbell/build.properties b/powerbell/build.properties index 3561d62..e90f1a0 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Dec 23 04:54:11 GMT 2025 +#Tue Dec 23 05:35:18 GMT 2025 stageCount=24 libraryProject= baseVersion=15.14 publishVersion=15.14.23 -buildCount=4 +buildCount=7 baseBetaVersion=15.14.24 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java index f094eb4..5113c7e 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java @@ -21,13 +21,13 @@ import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; +import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.dialogs.BackgroundPicturePreviewDialog; import cc.winboll.studio.powerbell.dialogs.ColorPaletteDialog; import cc.winboll.studio.powerbell.dialogs.NetworkBackgroundDialog; -import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog; import cc.winboll.studio.powerbell.models.BackgroundBean; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import cc.winboll.studio.powerbell.utils.BitmapCacheUtils; diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java index 5323c8f..9fc54c2 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/PixelPickerActivity.java @@ -1,9 +1,5 @@ package cc.winboll.studio.powerbell.activities; -/** - * @Author ZhanGSKen - * @Date 2025/06/22 14:15 - */ import android.app.Activity; import android.app.Dialog; import android.content.Intent; @@ -22,160 +18,191 @@ import android.widget.TextView; import android.widget.Toast; import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libaes.views.AToolbar; -import cc.winboll.studio.libappbase.GlobalApplication; +import cc.winboll.studio.libappbase.LogUtils; 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.models.BackgroundBean; import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +/** + * 像素拾取页面,支持加载图片并拾取指定位置像素颜色,同步至背景配置 + * 适配 API30,基于 Java7 开发 + * @Author ZhanGSKen + * @Date 2025/06/22 14:15 + */ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActivity { + // ======================== 静态常量 ========================= + public static final String TAG = "PixelPickerActivity"; + public static final String EXTRA_IMAGE_PATH = "imagePath"; // 图片路径传递键 + // 提示文本常量 + private static final String MSG_IMAGE_LOADED = "图片已加载,点击获取像素值"; + private static final String MSG_NO_IMAGE_PATH = "未找到图片路径"; + private static final String MSG_IMAGE_LOAD_FAILED = "图片加载失败"; + private static final String MSG_FILE_NOT_EXIST = "图片文件不存在"; + private static final String MSG_FILE_NOT_FOUND = "图片文件未找到"; + private static final String MSG_PIXEL_OUT_OF_RANGE = "像素坐标超出范围"; + private static final String MSG_TOUCH_OUT_OF_IMAGE = "点击位置超出图片显示范围"; + private static final String MSG_PIXEL_CALC_FAILED = "计算像素位置失败"; + private static final String MSG_PIXEL_RECORDED = "已记录像素值"; - public static final String TAG = "PixelPickerActivity"; + // ======================== 成员变量 ========================= + // UI组件 + private AToolbar mAToolbar; + private ImageView imageView; + private TextView infoText; + private ViewGroup imageContainer; + private RelativeLayout mainLayout; + // 图片与像素数据 + private Bitmap originalBitmap; // 原始图片Bitmap(用于像素拾取) - @Override - public Activity getActivity() { - return this; - } + // ======================== 接口实现方法 ========================= + @Override + public Activity getActivity() { + return this; + } - @Override - public String getTag() { - return TAG; - } - - private AToolbar mAToolbar; - private ImageView imageView; - private Bitmap originalBitmap; - private TextView infoText; - private ViewGroup imageContainer; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_pixelpicker); + @Override + public String getTag() { + return TAG; + } + // ======================== 生命周期方法 ========================= + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_pixelpicker); + LogUtils.d(TAG, "【onCreate】PixelPickerActivity 初始化开始"); + // 初始化UI组件 + initView(); // 初始化工具栏 + initToolbar(); + // 加载传递的图片 + loadImageFromIntent(); + // 绑定图片触摸事件 + bindImageTouchListener(); + + LogUtils.d(TAG, "【onCreate】PixelPickerActivity 初始化完成"); + } + + @Override + protected void onResume() { + super.onResume(); + LogUtils.d(TAG, "【onResume】PixelPickerActivity 恢复显示"); + // 同步背景颜色 + setBackgroundColor(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + // 回收Bitmap资源,避免内存泄漏 + if (originalBitmap != null && !originalBitmap.isRecycled()) { + originalBitmap.recycle(); + originalBitmap = null; + LogUtils.d(TAG, "【onDestroy】原始图片Bitmap资源已回收"); + } + LogUtils.d(TAG, "【onDestroy】PixelPickerActivity 销毁完成"); + } + + // ======================== UI初始化方法 ========================= + /** + * 初始化所有UI组件 + */ + private void initView() { mAToolbar = (AToolbar) findViewById(R.id.toolbar); + imageView = findViewById(R.id.imageView); + infoText = findViewById(R.id.infoText); + imageContainer = findViewById(R.id.imageContainer); + mainLayout = findViewById(R.id.activitypixelpickerRelativeLayout1); + + LogUtils.d(TAG, "【initView】UI组件初始化完成"); + } + + /** + * 初始化工具栏,设置导航与标题 + */ + private void initToolbar() { setActionBar(mAToolbar); mAToolbar.setSubtitle(R.string.subtitle_activity_pixelpicker); getActionBar().setDisplayHomeAsUpEnabled(true); mAToolbar.setNavigationOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); - - imageView = findViewById(R.id.imageView); - infoText = findViewById(R.id.infoText); - imageContainer = findViewById(R.id.imageContainer); - - // 从Intent获取图片路径并加载 - String imagePath = getIntent().getStringExtra("imagePath"); - if (imagePath != null) { - loadImage(imagePath); - } else { - infoText.setText("未找到图片路径"); - } - - // 设置图片点击事件 - imageContainer.setOnTouchListener(new View.OnTouchListener() { @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN && originalBitmap != null) { - // 计算点击位置在图片上的实际坐标 - float touchX = event.getX(); - float touchY = event.getY(); - - int pixelX = -1, pixelY = -1; - try { - // 获取图片在容器中的实际位置和尺寸 - int[] imageLocation = new int[2]; - imageView.getLocationInWindow(imageLocation); - int imageWidth = imageView.getWidth(); - int imageHeight = imageView.getHeight(); - - // 计算缩放比例 - float scaleX = (float) originalBitmap.getWidth() / imageWidth; - float scaleY = (float) originalBitmap.getHeight() / imageHeight; - - // 调整触摸坐标到图片坐标系 - float adjustedX = touchX - imageLocation[0]; - float adjustedY = touchY - imageLocation[1]; - - // 检查是否在图片范围内 - if (adjustedX >= 0 && adjustedX <= imageWidth && adjustedY >= 0 && adjustedY <= imageHeight) { - // 计算实际像素坐标 - pixelX = (int) (adjustedX * scaleX); - pixelY = (int) (adjustedY * scaleY); - - // 再次检查像素坐标是否在有效范围内 - if (pixelX >= 0 && pixelX < originalBitmap.getWidth() && - pixelY >= 0 && pixelY < originalBitmap.getHeight()) { - int pixelColor = originalBitmap.getPixel(pixelX, pixelY); - showPixelDialog(pixelColor, pixelX, pixelY); - } else { - Toast.makeText(PixelPickerActivity.this, "像素坐标超出范围", Toast.LENGTH_SHORT).show(); - } - } else { - Toast.makeText(PixelPickerActivity.this, "点击位置超出图片显示范围", Toast.LENGTH_SHORT).show(); - } - } catch (Exception e) { - e.printStackTrace(); - Toast.makeText(PixelPickerActivity.this, "计算像素位置失败", Toast.LENGTH_SHORT).show(); - } - } - return true; + public void onClick(View v) { + LogUtils.d(TAG, "【导航栏】点击返回"); + finish(); } }); - } + LogUtils.d(TAG, "【initToolbar】工具栏初始化完成"); + } - /** - * 加载图片 - */ - private void loadImage(String imagePath) { - try { - File file = new File(imagePath); - if (file.exists()) { - // 解码图片 - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = 1; // 加载原图 - originalBitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, options); + // ======================== 业务逻辑方法 ========================= + /** + * 从Intent中获取图片路径并加载图片 + */ + private void loadImageFromIntent() { + String imagePath = getIntent().getStringExtra(EXTRA_IMAGE_PATH); + LogUtils.d(TAG, "【loadImageFromIntent】获取到图片路径:" + imagePath); - if (originalBitmap != null) { - imageView.setImageBitmap(originalBitmap); - infoText.setText("图片已加载,点击获取像素值"); - } else { - infoText.setText("图片加载失败"); - } - } else { - infoText.setText("图片文件不存在"); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - infoText.setText("图片文件未找到"); - } - } + if (imagePath != null) { + loadImage(imagePath); + } else { + infoText.setText(MSG_NO_IMAGE_PATH); + LogUtils.w(TAG, "【loadImageFromIntent】未获取到图片路径"); + } + } - /** - * 显示像素对话框 - */ - private void showPixelDialog(final int pixelColor, int x, int y) { - final Dialog dialog = new Dialog(this); - dialog.setContentView(R.layout.dialog_pixel); - dialog.setCancelable(true); + /** + * 加载指定路径的图片 + * @param imagePath 图片文件路径 + */ + private void loadImage(String imagePath) { + try { + File file = new File(imagePath); + if (file.exists()) { + // 解码图片(加载原图) + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = 1; + originalBitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, options); - // 设置像素颜色视图背景 - TextView colorView = dialog.findViewById(R.id.pixelColorView); - colorView.setBackgroundColor(pixelColor); + if (originalBitmap != null) { + imageView.setImageBitmap(originalBitmap); + infoText.setText(MSG_IMAGE_LOADED); + LogUtils.d(TAG, "【loadImage】图片加载成功,尺寸:" + originalBitmap.getWidth() + "x" + originalBitmap.getHeight()); + } else { + infoText.setText(MSG_IMAGE_LOAD_FAILED); + LogUtils.e(TAG, "【loadImage】图片解码失败"); + } + } else { + infoText.setText(MSG_FILE_NOT_EXIST); + LogUtils.w(TAG, "【loadImage】图片文件不存在:" + imagePath); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + infoText.setText(MSG_FILE_NOT_FOUND); + LogUtils.e(TAG, "【loadImage】图片文件未找到:" + e.getMessage()); + } + } - // 显示颜色信息 - TextView infoText = dialog.findViewById(R.id.colorInfoText); - String colorInfo = String.format( + /** + * 显示像素颜色信息对话框 + * @param pixelColor 拾取的像素颜色(ARGB) + * @param x 像素X坐标 + * @param y 像素Y坐标 + */ + private void showPixelDialog(final int pixelColor, int x, int y) { + final Dialog dialog = new Dialog(this); + dialog.setContentView(R.layout.dialog_pixel); + dialog.setCancelable(true); + + // 设置颜色预览与信息展示 + TextView colorView = dialog.findViewById(R.id.pixelColorView); + TextView infoTextView = dialog.findViewById(R.id.colorInfoText); + colorView.setBackgroundColor(pixelColor); + + String colorInfo = String.format( "RGB: (%d, %d, %d)\n" + "ARGB: #%08X\n" + "实际像素位置: (%d, %d)", @@ -184,77 +211,129 @@ public class PixelPickerActivity extends WinBoLLActivity implements IWinBoLLActi Color.blue(pixelColor), pixelColor, x, y); - infoText.setText(colorInfo); + infoTextView.setText(colorInfo); + LogUtils.d(TAG, "【showPixelDialog】显示像素信息:" + colorInfo); - // 设置确定按钮点击事件 - Button confirmButton = dialog.findViewById(R.id.confirmButton); - confirmButton.setOnClickListener(new View.OnClickListener() { + // 确定按钮点击事件 + Button confirmButton = dialog.findViewById(R.id.confirmButton); + confirmButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); - // 可以在这里添加确定后的回调逻辑 - BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(PixelPickerActivity.this); - BackgroundBean bean = utils.getPreviewBackgroundBean(); - bean.setPixelColor(pixelColor); - utils.saveSettings(); - Toast.makeText(PixelPickerActivity.this, "已记录像素值", Toast.LENGTH_SHORT).show(); + // 保存像素颜色到背景配置 + savePixelColor(pixelColor); + Toast.makeText(PixelPickerActivity.this, MSG_PIXEL_RECORDED, Toast.LENGTH_SHORT).show(); + // 同步背景颜色 setBackgroundColor(); } }); - dialog.show(); - } + dialog.show(); + LogUtils.d(TAG, "【showPixelDialog】像素对话框已显示"); + } - @Override - protected void onDestroy() { - super.onDestroy(); - // 回收Bitmap资源 - if (originalBitmap != null && !originalBitmap.isRecycled()) { - originalBitmap.recycle(); - originalBitmap = null; - } - } + /** + * 保存拾取的像素颜色到背景配置 + * @param pixelColor 拾取的像素颜色(ARGB) + */ + private void savePixelColor(int pixelColor) { + BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this); + BackgroundBean bean = utils.getPreviewBackgroundBean(); + bean.setPixelColor(pixelColor); + utils.saveSettings(); + LogUtils.d(TAG, "【savePixelColor】像素颜色已保存:#" + Integer.toHexString(pixelColor)); + } + /** + * 同步背景颜色为拾取的像素颜色 + */ + void setBackgroundColor() { + BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this); + BackgroundBean bean = utils.getPreviewBackgroundBean(); + int pixelColor = bean.getPixelColor(); + mainLayout.setBackgroundColor(pixelColor); + LogUtils.d(TAG, "【setBackgroundColor】背景颜色已同步:#" + Integer.toHexString(pixelColor)); + } - void setBackgroundColor() { - BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(PixelPickerActivity.this); - BackgroundBean bean = utils.getPreviewBackgroundBean(); - int nPixelColor = bean.getPixelColor(); - RelativeLayout mainLayout = findViewById(R.id.activitypixelpickerRelativeLayout1); - mainLayout.setBackgroundColor(nPixelColor); - } + // ======================== 事件回调方法 ========================= + /** + * 绑定图片容器的触摸事件,处理像素拾取逻辑 + */ + private void bindImageTouchListener() { + imageContainer.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN && originalBitmap != null) { + float touchX = event.getX(); + float touchY = event.getY(); + LogUtils.v(TAG, "【onTouch】触摸坐标:(" + touchX + ", " + touchY + ")"); - @Override - protected void onResume() { - super.onResume(); - setBackgroundColor(); - } - + try { + // 获取图片在窗口中的位置与尺寸 + int[] imageLocation = new int[2]; + imageView.getLocationInWindow(imageLocation); + int imageWidth = imageView.getWidth(); + int imageHeight = imageView.getHeight(); + LogUtils.v(TAG, "【onTouch】图片显示尺寸:" + imageWidth + "x" + imageHeight + ",位置:(" + imageLocation[0] + ", " + imageLocation[1] + ")"); + + // 计算缩放比例 + float scaleX = (float) originalBitmap.getWidth() / imageWidth; + float scaleY = (float) originalBitmap.getHeight() / imageHeight; + LogUtils.v(TAG, "【onTouch】图片缩放比例:X=" + scaleX + ",Y=" + scaleY); + + // 调整触摸坐标到图片显示区域坐标系 + float adjustedX = touchX - imageLocation[0]; + float adjustedY = touchY - imageLocation[1]; + LogUtils.v(TAG, "【onTouch】调整后触摸坐标:(" + adjustedX + ", " + adjustedY + ")"); + + // 检查是否在图片显示范围内 + if (adjustedX >= 0 && adjustedX <= imageWidth && adjustedY >= 0 && adjustedY <= imageHeight) { + // 计算原始图片的像素坐标 + int pixelX = (int) (adjustedX * scaleX); + int pixelY = (int) (adjustedY * scaleY); + LogUtils.v(TAG, "【onTouch】计算后像素坐标:(" + pixelX + ", " + pixelY + ")"); + + // 检查像素坐标是否在原始图片范围内 + if (pixelX >= 0 && pixelX < originalBitmap.getWidth() && pixelY >= 0 && pixelY < originalBitmap.getHeight()) { + int pixelColor = originalBitmap.getPixel(pixelX, pixelY); + showPixelDialog(pixelColor, pixelX, pixelY); + } else { + Toast.makeText(PixelPickerActivity.this, MSG_PIXEL_OUT_OF_RANGE, Toast.LENGTH_SHORT).show(); + LogUtils.w(TAG, "【onTouch】像素坐标超出原始图片范围"); + } + } else { + Toast.makeText(PixelPickerActivity.this, MSG_TOUCH_OUT_OF_IMAGE, Toast.LENGTH_SHORT).show(); + LogUtils.w(TAG, "【onTouch】触摸位置超出图片显示范围"); + } + } catch (Exception e) { + e.printStackTrace(); + Toast.makeText(PixelPickerActivity.this, MSG_PIXEL_CALC_FAILED, Toast.LENGTH_SHORT).show(); + LogUtils.e(TAG, "【onTouch】计算像素位置失败:" + e.getMessage()); + } + } + return true; + } + }); + LogUtils.d(TAG, "【bindImageTouchListener】图片触摸事件已绑定"); + } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { - Intent intent = new Intent(); - intent.setClass(this, BackgroundSettingsActivity.class); + LogUtils.d(TAG, "【onOptionsItemSelected】点击返回菜单"); + Intent intent = new Intent(this, BackgroundSettingsActivity.class); startActivity(intent); - //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), ); return true; } - // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 return super.onOptionsItemSelected(item); } - @Override - public void onBackPressed() { - super.onBackPressed(); - setResult(RESULT_OK); - finish(); -// Intent intent = new Intent(); -// intent.setClass(this, BackgroundSettingsActivity.class); -// startActivity(intent); - //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), BackgroundPictureActivity.class); - } + @Override + public void onBackPressed() { + super.onBackPressed(); + setResult(RESULT_OK); + finish(); + LogUtils.d(TAG, "【onBackPressed】返回键触发,页面关闭"); + } } - - diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java index 6dd136b..4832d85 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java @@ -9,37 +9,58 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.R; /** + * 应用设置窗口,提供应用配置项的统一入口 + * 适配 API30,基于 Java7 开发 * @Author ZhanGSKen&豆包大模型 * @Date 2025/11/27 14:26 * @Describe 应用设置窗口 */ 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; + // ======================== 成员变量 ========================= + private Toolbar mToolbar; // 顶部工具栏 - @Override - public Activity getActivity() { - return this; - } + // ======================== 接口实现方法 ========================= + @Override + public Activity getActivity() { + return this; + } - @Override - public String getTag() { - return TAG; - } + @Override + public String getTag() { + return TAG; + } + // ======================== 生命周期方法 ========================= @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); + LogUtils.d(TAG, "【onCreate】SettingsActivity 初始化开始"); - mToolbar = findViewById(R.id.toolbar); + // 初始化工具栏 + initToolbar(); + + LogUtils.d(TAG, "【onCreate】SettingsActivity 初始化完成"); + } + + // ======================== UI初始化方法 ========================= + /** + * 初始化顶部工具栏,设置导航返回与样式 + */ + private void initToolbar() { + mToolbar = findViewById(R.id.toolbar); setSupportActionBar(mToolbar); - mToolbar.setSubtitle(getTag()); + // 设置工具栏副标题与标题样式 + mToolbar.setSubtitle(getTag()); mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + // 显示返回按钮 + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + // 绑定导航点击事件 mToolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -47,5 +68,7 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit finish(); } }); + LogUtils.d(TAG, "【initToolbar】工具栏初始化完成"); } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ShortcutActionActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ShortcutActionActivity.java index c22978d..e6e2fc4 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ShortcutActionActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/ShortcutActionActivity.java @@ -3,47 +3,73 @@ package cc.winboll.studio.powerbell.activities; import android.app.Activity; import android.content.Intent; import android.os.Bundle; +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.utils.APPPlusUtils; -import cc.winboll.studio.powerbell.App; /** + * 应用快捷方式活动类,处理应用图标快捷菜单的切换请求 + * 适配 API30,基于 Java7 开发 * @Author ZhanGSKen&豆包大模型 * @Date 2025/11/15 13:45 * @Describe 应用快捷方式活动类 */ public class ShortcutActionActivity extends Activity { - + // ======================== 静态常量 ========================= public static final String TAG = "ShortcutActionActivity"; + // 快捷指令常量 + private static final String ACTION_SWITCH_TO_EN1 = "switchto_en1"; + private static final String ACTION_SWITCH_TO_CN1 = "switchto_cn1"; + private static final String ACTION_SWITCH_TO_CN2 = "switchto_cn2"; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // 处理应用级别的切换请求 + // ======================== 生命周期方法 ========================= + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LogUtils.d(TAG, "【onCreate】ShortcutActionActivity 启动,开始处理快捷方式请求"); + + // 处理应用图标快捷菜单的切换请求 handleSwitchRequest(); - finish(); - } - /** - * 处理应用图标快捷菜单的请求 + LogUtils.d(TAG, "【onCreate】快捷方式请求处理完成,关闭活动"); + finish(); + } + + // ======================== 业务逻辑方法 ========================= + /** + * 处理应用图标快捷菜单的请求,根据意图数据切换应用启动组件 */ private void handleSwitchRequest() { Intent intent = getIntent(); - if (intent != null && "switchto_en1".equals(intent.getDataString())) { - APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_EN1); - ToastUtils.show("切换至" + getString(R.string.app_name) + "图标"); - //moveTaskToBack(true); + if (intent == null) { + LogUtils.w(TAG, "【handleSwitchRequest】意图为空,无法处理快捷方式请求"); + return; } - if (intent != null && "switchto_cn1".equals(intent.getDataString())) { - APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN1); - ToastUtils.show("切换至" + getString(R.string.app_name_cn1) + "图标"); - //moveTaskToBack(true); - } - if (intent != null && "switchto_cn2".equals(intent.getDataString())) { - APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN2); - ToastUtils.show("切换至" + getString(R.string.app_name_cn2) + "图标"); - //moveTaskToBack(true); + + String dataString = intent.getDataString(); + LogUtils.d(TAG, "【handleSwitchRequest】获取到快捷指令:" + dataString); + + // 匹配快捷指令并切换组件 + if (ACTION_SWITCH_TO_EN1.equals(dataString)) { + APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_EN1); + String toastMsg = "切换至" + getString(R.string.app_name) + "图标"; + ToastUtils.show(toastMsg); + LogUtils.d(TAG, "【handleSwitchRequest】已切换至EN1组件:" + App.COMPONENT_EN1); + } else if (ACTION_SWITCH_TO_CN1.equals(dataString)) { + APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN1); + String toastMsg = "切换至" + getString(R.string.app_name_cn1) + "图标"; + ToastUtils.show(toastMsg); + LogUtils.d(TAG, "【handleSwitchRequest】已切换至CN1组件:" + App.COMPONENT_CN1); + } else if (ACTION_SWITCH_TO_CN2.equals(dataString)) { + APPPlusUtils.switchAppLauncherToComponent(this, App.COMPONENT_CN2); + String toastMsg = "切换至" + getString(R.string.app_name_cn2) + "图标"; + ToastUtils.show(toastMsg); + LogUtils.d(TAG, "【handleSwitchRequest】已切换至CN2组件:" + App.COMPONENT_CN2); + } else { + LogUtils.w(TAG, "【handleSwitchRequest】未匹配到有效快捷指令:" + dataString); } } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java index 6ee6b24..ffbd227 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/WinBoLLActivity.java @@ -1,10 +1,5 @@ package cc.winboll.studio.powerbell.activities; -/** - * @Author ZhanGSKen - * @Date 2025/06/19 20:35 - * @Describe 应用窗口基类 - */ import android.annotation.SuppressLint; import android.app.Activity; import android.graphics.Color; @@ -24,107 +19,187 @@ import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libaes.models.AESThemeBean; import cc.winboll.studio.libaes.utils.AESThemeUtil; import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; +import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.BuildConfig; import cc.winboll.studio.powerbell.R; -@SuppressLint("SetTextI18n") +/** + * @Author ZhanGSKen + * @Date 2025/06/19 20:35 + * @Describe 应用窗口基类,提供主题设置、Activity 管理、工具栏配置、全屏切换、版本标签显示等通用功能 + * 适配 API30,基于 Java7 开发,所有子类需继承此类实现统一窗口行为 + */ public abstract class WinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity { - + // ======================== 静态常量 ========================= public static final String TAG = "WinBoLLActivity"; + private static final String VERSION_TAG_TEXT = "MIMO SDK V%s"; // 版本标签文本格式 + private static final float VERSION_TAG_TEXT_SIZE = 10f; // 版本标签字体大小(sp) - protected volatile AESThemeBean.ThemeType mThemeType; - protected TextView mTagView; - + // ======================== 成员变量 ========================= + protected volatile AESThemeBean.ThemeType mThemeType; // 当前主题类型 + protected TextView mTagView; // 版本标签显示控件 + + // ======================== 接口实现 & 抽象方法 ========================= + @Override + public abstract Activity getActivity(); + + @Override + public abstract String getTag(); + + // ======================== 生命周期方法 ========================= @Override protected void onCreate(Bundle savedInstanceState) { + LogUtils.d(TAG, String.format("【%s-onCreate】窗口基类初始化开始", getTag())); + // 初始化主题 mThemeType = getThemeType(); setThemeStyle(); super.onCreate(savedInstanceState); - } - - AESThemeBean.ThemeType getThemeType() { - return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext())); - } - - void setThemeStyle() { - setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext())); + LogUtils.d(TAG, String.format("【%s-onCreate】窗口基类初始化完成,当前主题:%s", getTag(), mThemeType)); } @Override protected void onStart() { super.onStart(); + LogUtils.d(TAG, String.format("【%s-onStart】添加版本标签到页面", getTag())); + // 添加版本标签 addVersionNameToContentView(); } + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + // 注册到Activity管理器 + WinBoLLActivityManager.getInstance().add(this); + LogUtils.d(TAG, String.format("【%s-onPostCreate】已注册到Activity管理器", getTag())); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + // 从Activity管理器移除 + WinBoLLActivityManager.getInstance().registeRemove(this); + LogUtils.d(TAG, String.format("【%s-onDestroy】已从Activity管理器移除", getTag())); + } + + // ======================== 主题相关方法 ========================= + /** + * 获取当前主题类型 + * @return 主题类型枚举 + */ + AESThemeBean.ThemeType getThemeType() { + int themeId = AESThemeUtil.getThemeTypeID(getApplicationContext()); + AESThemeBean.ThemeType themeType = AESThemeBean.getThemeStyleType(themeId); + LogUtils.d(TAG, String.format("【%s-getThemeType】获取主题类型,ID:%d,类型:%s", getTag(), themeId, themeType)); + return themeType; + } + + /** + * 设置主题样式 + */ + void setThemeStyle() { + int themeId = AESThemeUtil.getThemeTypeID(getApplicationContext()); + setTheme(themeId); + LogUtils.d(TAG, String.format("【%s-setThemeStyle】应用主题样式,ID:%d", getTag(), themeId)); + } + + // ======================== UI 配置方法 ========================= + /** + * 添加版本标签到页面底部 + */ protected void addVersionNameToContentView() { if (!isTagViewVisible()) { + LogUtils.d(TAG, String.format("【%s-addVersionNameToContentView】版本标签不可见,跳过添加", getTag())); return; } + if (mTagView == null) { mTagView = new TextView(this); + // 配置版本标签样式 mTagView.setTextColor(Color.GRAY); - mTagView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); - mTagView.setText("MIMO SDK V" + BuildConfig.VERSION_NAME); + mTagView.setTextSize(TypedValue.COMPLEX_UNIT_SP, VERSION_TAG_TEXT_SIZE); + mTagView.setText(String.format(VERSION_TAG_TEXT, BuildConfig.VERSION_NAME)); + // 配置布局参数 FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + // 添加到根布局 FrameLayout frameLayout = findViewById(android.R.id.content); - frameLayout.addView(mTagView, params); + if (frameLayout != null) { + frameLayout.addView(mTagView, params); + LogUtils.d(TAG, String.format("【%s-addVersionNameToContentView】版本标签添加完成,版本:%s", getTag(), BuildConfig.VERSION_NAME)); + } else { + LogUtils.w(TAG, String.format("【%s-addVersionNameToContentView】根布局为空,无法添加版本标签", getTag())); + } } } - protected boolean isTagViewVisible() { - return true; - } - + /** + * 配置工具栏,显示返回按钮 + */ public void setupToolbar() { Toolbar mToolbar = findViewById(R.id.toolbar); if (mToolbar != null) { setSupportActionBar(mToolbar); if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); + LogUtils.d(TAG, String.format("【%s-setupToolbar】工具栏配置完成,已显示返回按钮", getTag())); + } else { + LogUtils.w(TAG, String.format("【%s-setupToolbar】ActionBar为空,无法显示返回按钮", getTag())); } + } else { + LogUtils.w(TAG, String.format("【%s-setupToolbar】未找到工具栏控件(ID:toolbar)", getTag())); } } - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - WinBoLLActivityManager.getInstance().add(this); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - WinBoLLActivityManager.getInstance().registeRemove(this); + /** + * 版本标签是否可见 + * @return 默认为true,子类可重写修改 + */ + protected boolean isTagViewVisible() { + return true; } + // ======================== 菜单 & 返回键处理 ========================= @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { - //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class); + LogUtils.d(TAG, String.format("【%s-onOptionsItemSelected】点击返回菜单", getTag())); return true; } - // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 return super.onOptionsItemSelected(item); } - @Override - public void onBackPressed() { - super.onBackPressed(); - //GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class); - } - - public void changeFullScreen(Activity activity) { - Window window = activity.getWindow(); - if (window == null){ + @Override + public void onBackPressed() { + super.onBackPressed(); + LogUtils.d(TAG, String.format("【%s-onBackPressed】触发返回键", getTag())); + } + + // ======================== 工具方法 ========================= + /** + * 切换至全屏模式,隐藏状态栏与导航栏 + * @param activity 目标Activity + */ + public void changeFullScreen(Activity activity) { + if (activity == null) { + LogUtils.w(TAG, String.format("【%s-changeFullScreen】目标Activity为空,无法切换全屏", getTag())); return; } + + Window window = activity.getWindow(); + if (window == null) { + LogUtils.w(TAG, String.format("【%s-changeFullScreen】窗口为空,无法切换全屏", getTag())); + return; + } + View decorView = window.getDecorView(); - if (decorView == null){ + if (decorView == null) { + LogUtils.w(TAG, String.format("【%s-changeFullScreen】DecorView为空,无法切换全屏", getTag())); return; } + + // 配置全屏标志位 int flag = decorView.getSystemUiVisibility(); flag |= View.SYSTEM_UI_FLAG_FULLSCREEN; flag |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; @@ -132,6 +207,9 @@ public abstract class WinBoLLActivity extends AppCompatActivity implements IWinB flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; flag |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; decorView.setSystemUiVisibility(flag); + // 配置窗口标志位 window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + LogUtils.d(TAG, String.format("【%s-changeFullScreen】已切换至全屏模式", getTag())); } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/adapters/BatteryAdapter.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/adapters/BatteryAdapter.java index 2f78874..c87d328 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/adapters/BatteryAdapter.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/adapters/BatteryAdapter.java @@ -1,60 +1,106 @@ package cc.winboll.studio.powerbell.adapters; -/** - * @Author ZhanGSKen - * @Date 2025/03/22 14:38:55 - * @Describe 电池报告数据适配器 - */ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; +import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.R; -import cc.winboll.studio.powerbell.adapters.BatteryAdapter; import cc.winboll.studio.powerbell.models.BatteryData; import java.util.ArrayList; import java.util.List; +/** + * 电池报告数据适配器,用于RecyclerView展示电池电量、充放电时间数据 + * 适配 API30,基于 Java7 开发 + * @Author ZhanGSKen + * @Date 2025/03/22 14:38:55 + * @Describe 电池报告数据适配器 + */ public class BatteryAdapter extends RecyclerView.Adapter { + // ======================== 静态常量 ========================= public static final String TAG = "BatteryAdapter"; - private List dataList = new ArrayList<>(); + private static final String FORMAT_BATTERY_LEVEL = "%d%%"; // 电量显示格式 + private static final String PREFIX_DISCHARGE_TIME = "使用时间: "; // 放电时间前缀 + private static final String PREFIX_CHARGE_TIME = "充电时间: "; // 充电时间前缀 - public void updateData(List newData) { - dataList = newData; - notifyDataSetChanged(); + // ======================== 成员变量 ========================= + private List dataList = new ArrayList<>(); // 电池数据列表 + + // ======================== 构造方法 ========================= + public BatteryAdapter() { + LogUtils.d(TAG, "【BatteryAdapter】适配器初始化,初始数据列表为空"); } + // ======================== 数据操作方法 ========================= + /** + * 更新适配器数据并刷新列表 + * @param newData 新的电池数据列表 + */ + public void updateData(List newData) { + LogUtils.d(TAG, "【updateData】开始更新数据,新数据列表是否为空:" + (newData == null)); + // 判空处理,避免空指针 + if (newData != null) { + dataList = newData; + LogUtils.d(TAG, "【updateData】数据更新完成,当前数据量:" + dataList.size()); + } else { + dataList.clear(); + LogUtils.w(TAG, "【updateData】新数据列表为空,已清空本地数据"); + } + notifyDataSetChanged(); + LogUtils.d(TAG, "【updateData】已通知列表刷新"); + } + + // ======================== RecyclerView 重写方法 ========================= @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LogUtils.d(TAG, "【onCreateViewHolder】创建ViewHolder,父容器:" + parent.getContext().getClass().getSimpleName()); View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_battery_report, parent, false); - return new ViewHolder(view); + .inflate(R.layout.item_battery_report, parent, false); + ViewHolder viewHolder = new ViewHolder(view); + LogUtils.d(TAG, "【onCreateViewHolder】ViewHolder创建完成"); + return viewHolder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { + LogUtils.d(TAG, "【onBindViewHolder】绑定ViewHolder,位置:" + position); + // 判空与越界校验 + if (dataList == null || dataList.isEmpty() || position >= dataList.size()) { + LogUtils.w(TAG, "【onBindViewHolder】数据异常,无法绑定视图,位置:" + position); + return; + } + BatteryData item = dataList.get(position); - holder.tvLevel.setText(String.format("%d%%", item.getCurrentLevel())); - holder.tvDischargeTime.setText("使用时间: " + item.getDischargeTime()); - holder.tvChargeTime.setText("充电时间: " + item.getChargeTime()); + // 绑定数据到视图 + holder.tvLevel.setText(String.format(FORMAT_BATTERY_LEVEL, item.getCurrentLevel())); + holder.tvDischargeTime.setText(PREFIX_DISCHARGE_TIME + item.getDischargeTime()); + holder.tvChargeTime.setText(PREFIX_CHARGE_TIME + item.getChargeTime()); + + LogUtils.d(TAG, "【onBindViewHolder】视图绑定完成,位置:" + position + ",电量:" + item.getCurrentLevel() + "%"); } @Override public int getItemCount() { - return dataList.size(); + int count = dataList.size(); + LogUtils.d(TAG, "【getItemCount】获取条目数量:" + count); + return count; } + // ======================== ViewHolder 内部类 ========================= static class ViewHolder extends RecyclerView.ViewHolder { - TextView tvLevel; - TextView tvDischargeTime; - TextView tvChargeTime; + TextView tvLevel; // 电量显示 + TextView tvDischargeTime; // 放电时间显示 + TextView tvChargeTime; // 充电时间显示 ViewHolder(View itemView) { super(itemView); + // 初始化视图控件 tvLevel = itemView.findViewById(R.id.tvLevel); tvDischargeTime = itemView.findViewById(R.id.tvDischargeTime); tvChargeTime = itemView.findViewById(R.id.tvChargeTime); + LogUtils.d(TAG, "【ViewHolder】控件初始化完成"); } } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/BackgroundPicturePreviewDialog.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/BackgroundPicturePreviewDialog.java index d7fd091..1889114 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/BackgroundPicturePreviewDialog.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/BackgroundPicturePreviewDialog.java @@ -1,4 +1,5 @@ package cc.winboll.studio.powerbell.dialogs; + import android.app.Dialog; import android.content.Context; import android.content.Intent; @@ -11,130 +12,138 @@ 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.UriUtils; import cc.winboll.studio.powerbell.views.BackgroundView; -import java.io.File; /** + * 背景图片的接收分享文件后的预览对话框 + * 适配 API30,基于 Java7 开发,支持分享图片的Uri解析、预览与确认选择 * @Author ZhanGSKen * @Date 2024/04/25 16:27:53 * @Describe 背景图片的接收分享文件后的预览对话框 */ public class BackgroundPicturePreviewDialog extends Dialog { - + // ======================== 静态常量 ========================= public static final String TAG = "BackgroundPicturePreviewDialog"; + private static final String TOAST_MSG_EMPTY_FILE = "接收到的文件为空。"; // 空文件提示文本 - Context mContext; - //BackgroundSourceUtils mBackgroundPictureUtils; - Button dialogbackgroundpicturepreviewButton1; - Button dialogbackgroundpicturepreviewButton2; - //String mszPreReceivedFileName; - IOnRecivedPictureListener mIOnRecivedPictureListener; - Uri mUriRecivedPicture; - BackgroundView mBackgroundView; - - public BackgroundPicturePreviewDialog(Context context, IOnRecivedPictureListener iOnRecivedPictureListener) { - super(context); - setContentView(R.layout.dialog_backgroundpicturepreview); - mIOnRecivedPictureListener = iOnRecivedPictureListener; - //initEnv(); - - mContext = context; - //mBackgroundPictureUtils = BackgroundSourceUtils.getInstance(mContext); - - mBackgroundView = findViewById(R.id.backgroundview); - previewRecivedPicture(); - - dialogbackgroundpicturepreviewButton1 = findViewById(R.id.dialogbackgroundpicturepreviewButton1); - dialogbackgroundpicturepreviewButton1.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // 不使用分享到的图片 - // 跳转到主窗口 - Intent i = new Intent(mContext, MainActivity.class); - mContext.startActivity(i); - dismiss(); - } - }); - - dialogbackgroundpicturepreviewButton2 = findViewById(R.id.dialogbackgroundpicturepreviewButton2); - dialogbackgroundpicturepreviewButton2.setOnClickListener(new View.OnClickListener(){ - - @Override - public void onClick(View v) { - // 使用分享到的图片 - mIOnRecivedPictureListener.onAcceptRecivedPicture(mUriRecivedPicture); - // 关闭对话框 - dismiss(); - } - }); - } - -// void initEnv() { -// LogUtils.d(TAG, "initEnv()"); -// mszPreReceivedFileName = "PreReceived.data"; -// } - - void previewRecivedPicture() { - BackgroundSettingsActivity activity = ((BackgroundSettingsActivity)mContext); - - //取出文件uri - mUriRecivedPicture = activity.getIntent().getData(); - if (mUriRecivedPicture == null) { - mUriRecivedPicture = activity.getIntent().getParcelableExtra(Intent.EXTRA_STREAM); - } - //获取文件真实地址 - String szSrcImage = UriUtils.getFilePathFromUri(mContext, mUriRecivedPicture); - if (TextUtils.isEmpty(szSrcImage)) { - Toast.makeText(mContext, "接收到的文件为空。", Toast.LENGTH_SHORT).show(); - dismiss(); - return; - } - 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(); -// } -// } + // ======================== 成员变量 ========================= + private Context mContext; // 上下文对象 + private IOnRecivedPictureListener mIOnRecivedPictureListener; // 图片接收监听 + private Uri mUriRecivedPicture; // 接收的图片Uri + // 控件对象 + private BackgroundView mBackgroundView; // 背景预览视图 + private Button dialogbackgroundpicturepreviewButton1; // 取消按钮 + private Button dialogbackgroundpicturepreviewButton2; // 确认按钮 + // ======================== 接口定义 ========================= + /** + * 图片接收监听接口,用于通知确认选择的图片Uri + */ public interface IOnRecivedPictureListener { void onAcceptRecivedPicture(Uri uriRecivedPicture); } + // ======================== 构造方法 ========================= + public BackgroundPicturePreviewDialog(Context context, IOnRecivedPictureListener iOnRecivedPictureListener) { + super(context); + LogUtils.d(TAG, "【BackgroundPicturePreviewDialog】对话框初始化开始"); + // 初始化成员变量 + mContext = context; + mIOnRecivedPictureListener = iOnRecivedPictureListener; + + // 设置布局与控件 + setContentView(R.layout.dialog_backgroundpicturepreview); + initViews(); + bindButtonClickEvents(); + + // 预览接收的图片 + previewRecivedPicture(); + LogUtils.d(TAG, "【BackgroundPicturePreviewDialog】对话框初始化完成"); + } + + // ======================== 视图初始化方法 ========================= + /** + * 初始化对话框内所有控件 + */ + private void initViews() { + mBackgroundView = findViewById(R.id.backgroundview); + dialogbackgroundpicturepreviewButton1 = findViewById(R.id.dialogbackgroundpicturepreviewButton1); + dialogbackgroundpicturepreviewButton2 = findViewById(R.id.dialogbackgroundpicturepreviewButton2); + LogUtils.d(TAG, "【initViews】对话框控件初始化完成"); + } + + // ======================== 事件绑定方法 ========================= + /** + * 绑定按钮点击事件 + */ + private void bindButtonClickEvents() { + // 取消按钮:跳转到主页面并关闭对话框 + dialogbackgroundpicturepreviewButton1.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + LogUtils.d(TAG, "【onClick】点击取消按钮,跳转到主页面"); + Intent intent = new Intent(mContext, MainActivity.class); + mContext.startActivity(intent); + dismiss(); + LogUtils.d(TAG, "【onClick】对话框已关闭"); + } + }); + + // 确认按钮:通知监听并关闭对话框 + dialogbackgroundpicturepreviewButton2.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LogUtils.d(TAG, "【onClick】点击确认按钮,通知接收图片"); + if (mIOnRecivedPictureListener != null && mUriRecivedPicture != null) { + mIOnRecivedPictureListener.onAcceptRecivedPicture(mUriRecivedPicture); + LogUtils.d(TAG, "【onClick】已通知监听,图片Uri:" + mUriRecivedPicture); + } else { + LogUtils.w(TAG, "【onClick】监听为空或图片Uri无效,无法通知"); + } + dismiss(); + LogUtils.d(TAG, "【onClick】对话框已关闭"); + } + }); + LogUtils.d(TAG, "【bindButtonClickEvents】按钮点击事件绑定完成"); + } + + // ======================== 业务逻辑方法 ========================= + /** + * 预览接收的分享图片 + */ + private void previewRecivedPicture() { + LogUtils.d(TAG, "【previewRecivedPicture】开始预览接收的图片"); + // 校验上下文类型 + if (!(mContext instanceof BackgroundSettingsActivity)) { + LogUtils.e(TAG, "【previewRecivedPicture】上下文不是BackgroundSettingsActivity,无法获取图片Uri"); + Toast.makeText(mContext, TOAST_MSG_EMPTY_FILE, Toast.LENGTH_SHORT).show(); + dismiss(); + return; + } + + BackgroundSettingsActivity activity = (BackgroundSettingsActivity) mContext; + // 从Intent中获取图片Uri(优先getData,其次EXTRA_STREAM) + mUriRecivedPicture = activity.getIntent().getData(); + if (mUriRecivedPicture == null) { + mUriRecivedPicture = activity.getIntent().getParcelableExtra(Intent.EXTRA_STREAM); + LogUtils.d(TAG, "【previewRecivedPicture】从EXTRA_STREAM获取Uri:" + mUriRecivedPicture); + } else { + LogUtils.d(TAG, "【previewRecivedPicture】从getData获取Uri:" + mUriRecivedPicture); + } + + // 解析Uri为文件路径 + String szSrcImage = UriUtils.getFilePathFromUri(mContext, mUriRecivedPicture); + if (TextUtils.isEmpty(szSrcImage)) { + LogUtils.w(TAG, "【previewRecivedPicture】解析的文件路径为空"); + Toast.makeText(mContext, TOAST_MSG_EMPTY_FILE, Toast.LENGTH_SHORT).show(); + dismiss(); + return; + } + + // 加载图片到预览视图 + mBackgroundView.loadImage(szSrcImage); + LogUtils.d(TAG, "【previewRecivedPicture】图片预览完成,文件路径:" + szSrcImage); + } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java index 540f1d5..1371a4f 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java @@ -23,14 +23,17 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.R; import com.a4455jkjh.colorpicker.ColorPickerDialog; +import com.a4455jkjh.colorpicker.view.OnColorChangedListener; /** + * 调色板对话框(支持颜色拾取、RGB输入、透明度/亮度调节,兼容 API29-30+ 小米机型) + * 适配 API30,基于 Java7 开发,返回 0xAARRGGBB 格式颜色(含透明度) * @Author ZhanGSKen * @Date 2025/12/16 11:47 * @Describe 调色板对话框(支持颜色拾取、RGB输入、透明度/亮度调节,兼容 API29-30+ 小米机型) */ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, SeekBar.OnSeekBarChangeListener { - // ====================== 常量定义(首屏可见,统一管理) ====================== + // ====================== 静态常量(首屏可见,统一管理) ====================== public static final String TAG = "ColorPaletteDialog"; private static final int MAX_RGB_VALUE = 255; // RGB分量最大值(0-255) private static final int DEFAULT_BRIGHTNESS = 100; // 默认亮度百分比(100%,无调节) @@ -39,6 +42,8 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, private static final int MAX_BRIGHTNESS = 200; // 亮度最大值(200%,避免过曝失真) private static final int MAX_ALPHA_PERCENT = 100; // 透明度最大值(100%=不透明) private static final int MIN_ALPHA_PERCENT = 0; // 透明度最小值(0%=完全透明) + private static final String FORMAT_COLOR_HEX = "#%08X"; // 颜色值格式化(AARRGGBB) + private static final String FORMAT_PERCENT = "%d%%"; // 百分比格式化(X%) // ====================== 回调接口(紧跟常量,逻辑关联) ====================== public interface OnColorSelectedListener { @@ -66,9 +71,9 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, // 并发控制标记:是否是应用程序自身在更新颜色(避免循环回调/重复触发) private static volatile boolean isAppSelfUpdatingColor = false; - // 控件引用(新增透明度进度条+文本) + // 控件引用 private ImageView ivColorPicker; // 颜色预览拾取框 - private ImageView ivColorScaler; // 颜色渐变拾取框 + private ImageView ivColorScaler; // 颜色渐变拾取框 private EditText etR; // R分量输入框(显示实时调节值) private EditText etG; // G分量输入框(显示实时调节值) private EditText etB; // B分量输入框(显示实时调节值) @@ -93,12 +98,11 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, } // 2. 解析初始颜色:原始基准值 = 实时值(初始无调节) - // 透明度:初始颜色的alpha(0-255)转百分比(0-100%) this.mOriginalAlpha = Color.alpha(initialColor); this.mOriginalAlphaPercent = alpha2Percent(mOriginalAlpha); this.mCurrentAlpha = mOriginalAlpha; this.mCurrentAlphaPercent = mOriginalAlphaPercent; - // RGB:初始颜色的RGB分量 + this.mOriginalR = Color.red(initialColor); this.mOriginalG = Color.green(initialColor); this.mOriginalB = Color.blue(initialColor); @@ -110,10 +114,11 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, this.mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; this.mCurrentColor = initialColor; - LogUtils.d(TAG, "init dialog success | 初始颜色:" + String.format("#%08X", initialColor) - + " | 原始RGB:" + mOriginalR + "," + mOriginalG + "," + mOriginalB - + " | 原始透明度:" + mOriginalAlphaPercent + "%" - + " | 初始亮度:" + mCurrentBrightnessPercent + "%"); + LogUtils.d(TAG, String.format("init dialog success | 初始颜色:%s | 原始RGB:%d,%d,%d | 原始透明度:%s | 初始亮度:%s", + String.format(FORMAT_COLOR_HEX, initialColor), + mOriginalR, mOriginalG, mOriginalB, + String.format(FORMAT_PERCENT, mOriginalAlphaPercent), + String.format(FORMAT_PERCENT, mCurrentBrightnessPercent))); } // ====================== 生命周期方法(按执行顺序排列,逻辑清晰) ====================== @@ -142,11 +147,11 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, // ====================== 初始化核心方法(职责单一,便于维护) ====================== /** - * 控件绑定(新增透明度进度条+文本绑定) + * 控件绑定 */ private void initViewBind(View view) { ivColorPicker = view.findViewById(R.id.iv_color_picker); - ivColorScaler = view.findViewById(R.id.iv_color_scaler); + ivColorScaler = view.findViewById(R.id.iv_color_scaler); etR = view.findViewById(R.id.et_r); etG = view.findViewById(R.id.et_g); etB = view.findViewById(R.id.et_b); @@ -161,9 +166,9 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, // 控件非空校验(小米低版本容错,绑定失败直接关闭对话框) if (ivColorPicker == null || ivColorScaler == null || etR == null || etG == null || etB == null || etColorValue == null - || sbAlpha == null || tvAlphaValue == null - || tvBrightnessMinus == null || tvBrightnessValue == null || tvBrightnessPlus == null - || tvConfirm == null || tvCancel == null) { + || sbAlpha == null || tvAlphaValue == null + || tvBrightnessMinus == null || tvBrightnessValue == null || tvBrightnessPlus == null + || tvConfirm == null || tvCancel == null) { LogUtils.e(TAG, "view bind failed | 请检查布局ID是否正确!"); dismiss(); return; @@ -183,22 +188,23 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, etG.setText(String.valueOf(mCurrentG)); etB.setText(String.valueOf(mCurrentB)); - // 3. 颜色值输入框(显示当前最终颜色,格式#AARRGGBB,大写更规范) - etColorValue.setText(String.format("#%08X", mCurrentColor)); + // 3. 颜色值输入框(显示当前最终颜色,格式#AARRGGBB) + etColorValue.setText(String.format(FORMAT_COLOR_HEX, mCurrentColor)); // 4. 透明度控件(进度条+文本,初始=原始透明度) sbAlpha.setProgress(mCurrentAlphaPercent); - tvAlphaValue.setText(mCurrentAlphaPercent + "%"); + tvAlphaValue.setText(String.format(FORMAT_PERCENT, mCurrentAlphaPercent)); // 5. 亮度控件(显示默认100%,初始化按钮状态) - tvBrightnessValue.setText(mCurrentBrightnessPercent + "%"); - updateBrightnessBtnStatus(); // 禁用边界值按钮(初始100%,都可用) + tvBrightnessValue.setText(String.format(FORMAT_PERCENT, mCurrentBrightnessPercent)); + updateBrightnessBtnStatus(); // 禁用边界值按钮 - LogUtils.d(TAG, "init data complete | 原始透明度:" + mOriginalAlphaPercent + "%"); + LogUtils.d(TAG, String.format("init data complete | 原始透明度:%s", + String.format(FORMAT_PERCENT, mOriginalAlphaPercent))); } /** - * 监听初始化(新增透明度进度条监听) + * 监听初始化 */ private void initListener() { // 点击监听(按钮+颜色拾取框) @@ -225,10 +231,10 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, // 宽度占屏幕80%,高度自适应(适配不同屏幕尺寸) lp.width = (int) (getContext().getResources().getDisplayMetrics().widthPixels * 0.8); lp.height = WindowManager.LayoutParams.WRAP_CONTENT; - // 软键盘适配:小米虚拟导航栏兼容,避免输入框被遮挡 + // 软键盘适配:小米虚拟导航栏兼容 window.setAttributes(lp); window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN - | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); LogUtils.d(TAG, "dialog size adjust complete | 适配全面屏+软键盘"); } } @@ -261,7 +267,7 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, }); } - // ====================== 透明度进度条监听实现(核心新增) ====================== + // ====================== 透明度进度条监听实现 ====================== @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // 仅处理用户手动拖动进度条(避免应用自身更新时触发) @@ -277,7 +283,7 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, public void onStopTrackingTouch(SeekBar seekBar) {} /** - * 拖动透明度进度条更新颜色(核心新增逻辑) + * 拖动透明度进度条更新颜色 */ private synchronized void updateAlphaBySeekBar(int alphaPercent) { if (!isAppSelfUpdatingColor) { @@ -290,18 +296,17 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, calculateBrightnessAndUpdate(); // 同步所有控件 updateAllViews(); - LogUtils.d(TAG, "update alpha by seekbar | 透明度:" + mCurrentAlphaPercent + "%"); + LogUtils.d(TAG, String.format("update alpha by seekbar | 透明度:%s", + String.format(FORMAT_PERCENT, mCurrentAlphaPercent))); } finally { - // 直接释放标记,避免卡顿 - isAppSelfUpdatingColor = false; + isAppSelfUpdatingColor = false; // 释放标记 } } } - // ====================== 颜色核心逻辑(新增透明度参数,全功能兼容) ====================== + // ====================== 颜色核心逻辑 ====================== /** * 核心计算:基于原始RGB+当前亮度+当前透明度,计算实时RGB+最终颜色 - * 逻辑:亮度百分比→调节系数→原始RGB×系数→限制0-255→拼接透明度→最终颜色 */ private void calculateBrightnessAndUpdate() { // 亮度百分比转调节系数(10%→0.1,100%→1.0,200%→2.0) @@ -317,98 +322,93 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, } /** - * 亮度减少(每次减5%,最低10%,防止过暗) + * 亮度减少(每次减5%,最低10%) */ private void decreaseBrightness() { changeBrightness(false); } /** - * 亮度增加(每次加5%,最高200%,防止过曝) + * 亮度增加(每次加5%,最高200%) */ private void increaseBrightness() { changeBrightness(true); } /** - * 亮度调节核心方法(统一逻辑,加并发控制,同步所有控件) + * 亮度调节核心方法(统一逻辑,加并发控制) */ private synchronized void changeBrightness(boolean isIncrease) { - // 关键:判断非应用自身更新,才执行调节(避免重复触发/循环) if (!isAppSelfUpdatingColor) { - isAppSelfUpdatingColor = true; // 标记为应用自身更新 + isAppSelfUpdatingColor = true; try { if (isIncrease) { - if (mCurrentBrightnessPercent >= MAX_BRIGHTNESS) return; // 达到最大值,不处理 - mCurrentBrightnessPercent += BRIGHTNESS_STEP; // 增加步长 + if (mCurrentBrightnessPercent >= MAX_BRIGHTNESS) return; + mCurrentBrightnessPercent += BRIGHTNESS_STEP; } else { - if (mCurrentBrightnessPercent <= MIN_BRIGHTNESS) return; // 达到最小值,不处理 - mCurrentBrightnessPercent -= BRIGHTNESS_STEP; // 减少步长 + if (mCurrentBrightnessPercent <= MIN_BRIGHTNESS) return; + mCurrentBrightnessPercent -= BRIGHTNESS_STEP; } - // 计算亮度调节后的实时RGB+最终颜色(含当前透明度) + // 计算亮度调节后的实时RGB+最终颜色 calculateBrightnessAndUpdate(); // 同步所有控件 updateAllViews(); - LogUtils.d(TAG, (isIncrease ? "increase" : "decrease") + " brightness | " - + "亮度:" + mCurrentBrightnessPercent + "% | 实时RGB:" + mCurrentR + "," + mCurrentG + "," + mCurrentB); + LogUtils.d(TAG, String.format("%s brightness | 亮度:%s | 实时RGB:%d,%d,%d", + isIncrease ? "increase" : "decrease", + String.format(FORMAT_PERCENT, mCurrentBrightnessPercent), + mCurrentR, mCurrentG, mCurrentB)); } finally { - // 直接释放标记,避免卡顿 isAppSelfUpdatingColor = false; } } } /** - * 解析颜色字符串(支持#RRGGBB/#AARRGGBB,容错处理,更新原始基准值+实时值) - * 新增:解析颜色的透明度,同步更新透明度进度条 + * 解析颜色字符串(支持#RRGGBB/#AARRGGBB,更新原始基准值+实时值) */ private void parseColorFromStr(String colorStr, int triggerViewId) { - // 关键:判断非应用自身更新,才执行解析(避免循环回调) if (!isAppSelfUpdatingColor) { - isAppSelfUpdatingColor = true; // 标记为应用自身更新 + isAppSelfUpdatingColor = true; try { if (TextUtils.isEmpty(colorStr)) return; - // 补全#前缀(兼容用户输入习惯,如直接输AARRGGBB) + // 补全#前缀(兼容用户输入习惯) if (!colorStr.startsWith("#")) { colorStr = "#" + colorStr; } - // 格式校验(仅支持6位RRGGBB/8位AARRGGBB,避免非法格式) + // 格式校验(仅支持6位RRGGBB/8位AARRGGBB) if (colorStr.length() != 7 && colorStr.length() != 9) { - LogUtils.e(TAG, "parse color failed | 格式错误(需#RRGGBB/#AARRGGBB),输入:" + colorStr); + LogUtils.e(TAG, String.format("parse color failed | 格式错误(需#RRGGBB/#AARRGGBB),输入:%s", colorStr)); return; } - // 解析颜色(系统API,安全可靠) + // 解析颜色 int parsedColor = Color.parseColor(colorStr); - // 更新原始基准值(用户输入颜色,重置基准) - // 透明度:解析颜色的alpha(0-255)转百分比(0-100%) + // 更新原始基准值与实时值 mOriginalAlpha = Color.alpha(parsedColor); mOriginalAlphaPercent = alpha2Percent(mOriginalAlpha); - // RGB:解析颜色的RGB分量 mOriginalR = Color.red(parsedColor); mOriginalG = Color.green(parsedColor); mOriginalB = Color.blue(parsedColor); - // 更新实时值(原始值=实时值,无调节) mCurrentAlpha = mOriginalAlpha; mCurrentAlphaPercent = mOriginalAlphaPercent; mCurrentR = mOriginalR; mCurrentG = mOriginalG; mCurrentB = mOriginalB; - // 重置亮度为100% mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; mCurrentColor = parsedColor; // 同步所有控件 updateAllViews(); - LogUtils.d(TAG, "parse color success | 解析颜色:" + String.format("#%08X", parsedColor) - + " | 透明度:" + mCurrentAlphaPercent + "% | 重置亮度:" + DEFAULT_BRIGHTNESS + "%"); + LogUtils.d(TAG, String.format("parse color success | 解析颜色:%s | 透明度:%s | 重置亮度:%s", + String.format(FORMAT_COLOR_HEX, parsedColor), + String.format(FORMAT_PERCENT, mCurrentAlphaPercent), + String.format(FORMAT_PERCENT, DEFAULT_BRIGHTNESS))); } catch (IllegalArgumentException e) { - LogUtils.e(TAG, "parse color failed | 非法颜色格式,输入:" + colorStr, e); + LogUtils.e(TAG, String.format("parse color failed | 非法颜色格式,输入:%s", colorStr), e); } finally { - // 直接释放标记,避免卡顿 isAppSelfUpdatingColor = false; } } @@ -416,77 +416,74 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, /** * 通过RGB输入框更新颜色(用户输入后,更新原始基准值+实时值,重置亮度为100%) - * 新增:透明度基准值保持不变,仅更新RGB */ private synchronized void updateColorByRGB(int triggerViewId) { - // 关键:判断非应用自身更新,才执行更新(避免循环回调) if (!isAppSelfUpdatingColor) { - isAppSelfUpdatingColor = true; // 标记为应用自身更新 + isAppSelfUpdatingColor = true; try { // 解析用户输入的RGB值(限制0-255,非法输入设为0) int inputR = parseInputValue(etR.getText().toString()); int inputG = parseInputValue(etG.getText().toString()); int inputB = parseInputValue(etB.getText().toString()); - // 更新原始基准值(用户手动输入,作为新的调节基准) + // 更新原始基准值与实时值 mOriginalR = inputR; mOriginalG = inputG; mOriginalB = inputB; - // 更新实时值(输入值=实时值,无亮度调节) mCurrentR = inputR; mCurrentG = inputG; mCurrentB = inputB; - // 重置亮度为100%(透明度保持当前值不变) mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; - // 计算最终颜色(无亮度调节,拼接当前透明度) mCurrentColor = Color.argb(mCurrentAlpha, mCurrentR, mCurrentG, mCurrentB); // 同步所有控件 updateAllViews(); - LogUtils.d(TAG, "update color by RGB | 新原始RGB:" + mOriginalR + "," + mOriginalG + "," + mOriginalB - + " | 透明度:" + mCurrentAlphaPercent + "% | 重置亮度:" + DEFAULT_BRIGHTNESS + "%"); + LogUtils.d(TAG, String.format("update color by RGB | 新原始RGB:%d,%d,%d | 透明度:%s | 重置亮度:%s", + mOriginalR, mOriginalG, mOriginalB, + String.format(FORMAT_PERCENT, mCurrentAlphaPercent), + String.format(FORMAT_PERCENT, DEFAULT_BRIGHTNESS))); } catch (Exception e) { LogUtils.e(TAG, "update color by RGB failed", e); } finally { - // 直接释放标记,避免卡顿 isAppSelfUpdatingColor = false; } } } /** - * 核心同步:更新所有控件显示(新增透明度控件同步,统一方法) + * 核心同步:更新所有控件显示 */ private void updateAllViews() { - // 1. 同步颜色预览(显示最终颜色,含透明度+亮度) + // 1. 同步颜色预览 ivColorPicker.setBackgroundColor(mCurrentColor); - // 2. 同步RGB输入框(显示实时调节值) + // 2. 同步RGB输入框 etR.setText(String.valueOf(mCurrentR)); etG.setText(String.valueOf(mCurrentG)); etB.setText(String.valueOf(mCurrentB)); - // 3. 同步颜色值输入框(显示最终颜色,含透明度,格式#AARRGGBB) - etColorValue.setText(String.format("#%08X", mCurrentColor)); + // 3. 同步颜色值输入框 + etColorValue.setText(String.format(FORMAT_COLOR_HEX, mCurrentColor)); - // 4. 同步透明度控件(进度条+文本,显示实时透明度) + // 4. 同步透明度控件 sbAlpha.setProgress(mCurrentAlphaPercent); - tvAlphaValue.setText(mCurrentAlphaPercent + "%"); + tvAlphaValue.setText(String.format(FORMAT_PERCENT, mCurrentAlphaPercent)); - // 5. 同步亮度控件(数值+按钮状态) - tvBrightnessValue.setText(mCurrentBrightnessPercent + "%"); + // 5. 同步亮度控件 + tvBrightnessValue.setText(String.format(FORMAT_PERCENT, mCurrentBrightnessPercent)); updateBrightnessBtnStatus(); - LogUtils.d(TAG, "sync all views complete | 最终颜色:" + String.format("#%08X", mCurrentColor) - + " | 实时RGB:" + mCurrentR + "," + mCurrentG + "," + mCurrentB - + " | 透明度:" + mCurrentAlphaPercent + "% | 亮度:" + mCurrentBrightnessPercent + "%"); + LogUtils.d(TAG, String.format("sync all views complete | 最终颜色:%s | 实时RGB:%d,%d,%d | 透明度:%s | 亮度:%s", + String.format(FORMAT_COLOR_HEX, mCurrentColor), + mCurrentR, mCurrentG, mCurrentB, + String.format(FORMAT_PERCENT, mCurrentAlphaPercent), + String.format(FORMAT_PERCENT, mCurrentBrightnessPercent))); } /** * 更新亮度按钮状态(边界值禁用,提升交互体验) */ private void updateBrightnessBtnStatus() { - // 亮度≤10%:禁用减号(文字变浅灰);≥200%:禁用加号(文字变浅灰) boolean canMinus = mCurrentBrightnessPercent > MIN_BRIGHTNESS; boolean canPlus = mCurrentBrightnessPercent < MAX_BRIGHTNESS; @@ -496,37 +493,37 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, tvBrightnessPlus.setTextColor(canPlus ? Color.BLACK : Color.parseColor("#CCCCCC")); } - // ====================== 工具方法(新增透明度转换工具,通用复用) ====================== + // ====================== 工具方法 ====================== /** - * 透明度:0-255 → 0-100%(颜色计算值转用户直观百分比) + * 透明度:0-255 → 0-100% */ private int alpha2Percent(int alpha) { return Math.round((float) alpha / MAX_RGB_VALUE * MAX_ALPHA_PERCENT); } /** - * 透明度:0-100% → 0-255(用户操作百分比转颜色计算值) + * 透明度:0-100% → 0-255 */ private int percent2Alpha(int percent) { return Math.round((float) percent / MAX_ALPHA_PERCENT * MAX_RGB_VALUE); } /** - * 解析输入值(限制0-255,非法输入返回0,容错处理) + * 解析输入值(限制0-255,非法输入返回0) */ private int parseInputValue(String input) { if (TextUtils.isEmpty(input)) return 0; try { int value = Integer.parseInt(input); - return Math.min(Math.max(value, 0), MAX_RGB_VALUE); // 限制范围,避免溢出 + return Math.min(Math.max(value, 0), MAX_RGB_VALUE); } catch (NumberFormatException e) { - LogUtils.e(TAG, "parse input failed | 非法数字,输入:" + input, e); + LogUtils.e(TAG, String.format("parse input failed | 非法数字,输入:%s", input), e); return 0; } } /** - * RGB输入框监听复用(减少冗余代码,统一逻辑) + * RGB输入框监听复用 */ private void setEditTextWatcher(EditText editText, final int viewId) { editText.addTextChangedListener(new TextWatcher() { @@ -538,90 +535,77 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, @Override public void afterTextChanged(Editable s) { - // 关键:判断非应用自身更新,才执行更新(避免循环回调) if (!isAppSelfUpdatingColor) { - updateColorByRGB(viewId); // 输入变化后更新颜色 + updateColorByRGB(viewId); } } }); } /** - * dp转px(适配小米不同分辨率,避免尺寸错乱,通用工具) + * dp转px(适配小米不同分辨率) */ private int dp2px(float dp) { return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f); } - /** - * 显示系统颜色选择器(兼容API29-30,无高版本依赖,小米机型适配) - * 核心调整:新增「水平滚动容器+颜色排列容器」二级结构,内置圆形按钮,无额外drawable依赖 - */ - private void showSystemColorPicker() { - LogUtils.d(TAG, "show system color picker | 兼容小米API29-30,支持横向滚动"); - final android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(getContext()); - builder.setTitle("选择基础颜色"); + /** + * 显示系统颜色选择器(兼容API29-30,无高版本依赖,小米机型适配) + */ + private void showSystemColorPicker() { + LogUtils.d(TAG, "show system color picker | 兼容小米API29-30,支持横向滚动"); + final android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(getContext()); + builder.setTitle("选择基础颜色"); - // 50种常用颜色:按「红→橙→黄→绿→青→蓝→紫→粉→棕→灰→黑白」彩虹光谱顺序排列 + // 50种常用颜色:按彩虹光谱顺序排列 final int[] systemColors = { - // 红色系(6种:深红→大红→浅红→玫红→暗红→橘红) - 0xFFCC0000, 0xFFFF0000, 0xFFFF6666, 0xFFFF1493, 0xFF8B0000, 0xFFFF4500, - // 橙色系(5种:深橙→橙→浅橙→橙黄→橘橙) - 0xFFCC6600, 0xFFFF8800, 0xFFFFAA33, 0xFFFFBB00, 0xFFF5A623, - // 黄色系(5种:深黄→黄→浅黄→鹅黄→金黄) - 0xFFCCCC00, 0xFFFFFF00, 0xFFFFEE99, 0xFFFFFACD, 0xFFFFD700, - // 绿色系(7种:深绿→绿→浅绿→草绿→薄荷绿→翠绿→墨绿) - 0xFF006600, 0xFF00FF00, 0xFF99FF99, 0xFF66CC66, 0xFF98FB98, 0xFF00FF99, 0xFF003300, - // 青色系(5种:深青→青→浅青→蓝绿→青绿) - 0xFF006666, 0xFF00FFFF, 0xFF99FFFF, 0xFF00CCCC, 0xFF40E0D0, - // 蓝色系(8种:深蓝→藏蓝→蓝→浅蓝→天蓝→宝蓝→湖蓝→靛蓝) - 0xFF0000CC, 0xFF00008B, 0xFF0000FF, 0xFF6666FF, 0xFF87CEEB, 0xFF0066FF, 0xFF0099FF, 0xFF4B0082, - // 紫色系(6种:深紫→紫→浅紫→紫罗兰→紫红→蓝紫) - 0xFF660099, 0xFF8800FF, 0xFFAA99FF, 0xFF9370DB, 0xFFCBC3E3, 0xFF8A2BE2, - // 粉色系(5种:深粉→粉→浅粉→嫩粉→桃粉) - 0xFFFF00FF, 0xFFFF99CC, 0xFFFFCCDD, 0xFFFFB6C1, 0xFFFFA5A5, - // 棕色系(4种:深棕→棕→浅棕→棕黄) - 0xFF8B4513, 0xFFA0522D, 0xFFD2B48C, 0xFFCD853F, - // 灰色系(6种:深灰→灰→浅灰→银灰→淡灰→浅银灰) - 0xFF333333, 0xFF666666, 0xFF888888, 0xFFAAAAAA, 0xFFCCCCCC, 0xFFE6E6E6, - // 黑白系(3种:黑→白→米白) - 0xFF000000, 0xFFFFFFFF, 0xFFFFFAFA + 0xFFCC0000, 0xFFFF0000, 0xFFFF6666, 0xFFFF1493, 0xFF8B0000, 0xFFFF4500, + 0xFFCC6600, 0xFFFF8800, 0xFFFFAA33, 0xFFFFBB00, 0xFFF5A623, + 0xFFCCCC00, 0xFFFFFF00, 0xFFFFEE99, 0xFFFFFACD, 0xFFFFD700, + 0xFF006600, 0xFF00FF00, 0xFF99FF99, 0xFF66CC66, 0xFF98FB98, 0xFF00FF99, 0xFF003300, + 0xFF006666, 0xFF00FFFF, 0xFF99FFFF, 0xFF00CCCC, 0xFF40E0D0, + 0xFF0000CC, 0xFF00008B, 0xFF0000FF, 0xFF6666FF, 0xFF87CEEB, 0xFF0066FF, 0xFF0099FF, 0xFF4B0082, + 0xFF660099, 0xFF8800FF, 0xFFAA99FF, 0xFF9370DB, 0xFFCBC3E3, 0xFF8A2BE2, + 0xFFFF00FF, 0xFFFF99CC, 0xFFFFCCDD, 0xFFFFB6C1, 0xFFFFA5A5, + 0xFF8B4513, 0xFFA0522D, 0xFFD2B48C, 0xFFCD853F, + 0xFF333333, 0xFF666666, 0xFF888888, 0xFFAAAAAA, 0xFFCCCCCC, 0xFFE6E6E6, + 0xFF000000, 0xFFFFFFFF, 0xFFFFFAFA }; - // 1. 第一级:水平滚动容器 - HorizontalScrollView horizontalScrollView = new HorizontalScrollView(getContext()); - horizontalScrollView.setHorizontalScrollBarEnabled(true); - horizontalScrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); - horizontalScrollView.setPadding(dp2px(5), dp2px(5), dp2px(5), dp2px(5)); + // 1. 第一级:水平滚动容器 + HorizontalScrollView horizontalScrollView = new HorizontalScrollView(getContext()); + horizontalScrollView.setHorizontalScrollBarEnabled(true); + horizontalScrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); + horizontalScrollView.setPadding(dp2px(5), dp2px(5), dp2px(5), dp2px(5)); - // 2. 第二级:颜色排列容器(横向) - LinearLayout colorLayout = new LinearLayout(getContext()); - colorLayout.setOrientation(LinearLayout.HORIZONTAL); - colorLayout.setGravity(Gravity.CENTER_VERTICAL); - colorLayout.setPadding(dp2px(10), dp2px(10), dp2px(10), dp2px(10)); + // 2. 第二级:颜色排列容器(横向) + LinearLayout colorLayout = new LinearLayout(getContext()); + colorLayout.setOrientation(LinearLayout.HORIZONTAL); + colorLayout.setGravity(Gravity.CENTER_VERTICAL); + colorLayout.setPadding(dp2px(10), dp2px(10), dp2px(10), dp2px(10)); - // 3. 循环添加颜色按钮(内置圆形效果,无额外依赖) - for (int i = 0; i < systemColors.length; i++) { - final int color = systemColors[i]; - ImageView colorBtn = new ImageView(getContext()); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(dp2px(40), dp2px(40)); - if (i != systemColors.length - 1) { - lp.setMargins(0, 0, dp2px(10), 0); // 按钮间距 - } - colorBtn.setLayoutParams(lp); + // 3. 循环添加颜色按钮(内置圆形效果) + for (int i = 0; i < systemColors.length; i++) { + final int color = systemColors[i]; + ImageView colorBtn = new ImageView(getContext()); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(dp2px(40), dp2px(40)); + if (i != systemColors.length - 1) { + lp.setMargins(0, 0, dp2px(10), 0); // 按钮间距 + } + colorBtn.setLayoutParams(lp); - // 核心:内置圆形背景(白色边框+圆形形状,无需drawable文件) - GradientDrawable circleBg = new GradientDrawable(); - circleBg.setShape(GradientDrawable.OVAL); // 圆形 - circleBg.setColor(color); // 按钮颜色 - circleBg.setStroke(dp2px(2), Color.WHITE); // 白色边框(2dp宽,区分颜色) - colorBtn.setBackground(circleBg); // 设置圆形背景 + // 内置圆形背景(白色边框+圆形形状) + GradientDrawable circleBg = new GradientDrawable(); + circleBg.setShape(GradientDrawable.OVAL); + circleBg.setColor(color); + circleBg.setStroke(dp2px(2), Color.WHITE); + colorBtn.setBackground(circleBg); - colorBtn.setClickable(true); - colorBtn.setFocusable(true); + colorBtn.setClickable(true); + colorBtn.setFocusable(true); - // 点击事件(逻辑不变) - colorBtn.setOnClickListener(new View.OnClickListener() { + // 点击事件 + colorBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isAppSelfUpdatingColor) { @@ -641,57 +625,59 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, mCurrentColor = color; updateAllViews(); builder.create().dismiss(); - LogUtils.d(TAG, "select system color | 选择颜色:" + String.format("#%08X", color) - + " | 透明度:" + mCurrentAlphaPercent + "%"); + LogUtils.d(TAG, String.format("select system color | 选择颜色:%s | 透明度:%s", + String.format(FORMAT_COLOR_HEX, color), + String.format(FORMAT_PERCENT, mCurrentAlphaPercent))); } finally { isAppSelfUpdatingColor = false; } } } }); - colorLayout.addView(colorBtn); - } + colorLayout.addView(colorBtn); + } - // 层级嵌套(滚动容器→颜色容器) - horizontalScrollView.addView(colorLayout); - builder.setView(horizontalScrollView).setNegativeButton("关闭", null).show(); - } + // 层级嵌套 + horizontalScrollView.addView(colorLayout); + builder.setView(horizontalScrollView).setNegativeButton("关闭", null).show(); + } - // ====================== 点击事件实现(统一处理,逻辑清晰) ====================== + // ====================== 点击事件实现 ====================== @Override public void onClick(View v) { - //ToastUtils.show("onClick"); int id = v.getId(); - // 关键:所有点击事件均加判断(避免并发冲突/重复触发) + // 所有点击事件均加并发判断 if (!isAppSelfUpdatingColor) { if (id == R.id.iv_color_picker) { - showSystemColorPicker(); // 打开系统颜色选择器 - } if (id == R.id.iv_color_scaler) { - //ToastUtils.show("iv_color_scale"); - openColorScalerDialog(mCurrentColor); // 打开系统颜色选择器 + showSystemColorPicker(); + } else if (id == R.id.iv_color_scaler) { + openColorScalerDialog(mCurrentColor); } else if (id == R.id.tv_confirm) { - mListener.onColorSelected(mCurrentColor); // 确认选择,回调颜色 - LogUtils.d(TAG, "confirm color | 回调颜色:" + String.format("#%08X", mCurrentColor)); + mListener.onColorSelected(mCurrentColor); + LogUtils.d(TAG, String.format("confirm color | 回调颜色:%s", + String.format(FORMAT_COLOR_HEX, mCurrentColor))); dismiss(); } else if (id == R.id.tv_cancel) { - dismiss(); // 取消,关闭对话框 + dismiss(); LogUtils.d(TAG, "cancel color | 取消选择,关闭对话框"); } else if (id == R.id.tv_brightness_minus) { - decreaseBrightness(); // 减少亮度 + decreaseBrightness(); } else if (id == R.id.tv_brightness_plus) { - increaseBrightness(); // 增加亮度 + increaseBrightness(); } } } - void openColorScalerDialog(int nColor) { - //ToastUtils.show("openColorPickerDialog"); - final ColorScalerDialog dlg = new ColorScalerDialog(getContext(), nColor); - dlg.setOnColorChangedListener(new com.a4455jkjh.colorpicker.view.OnColorChangedListener() { - + /** + * 打开颜色渐变选择器 + */ + void openColorScalerDialog(int nColor) { + LogUtils.d(TAG, String.format("openColorScalerDialog | 初始颜色:%s", + String.format(FORMAT_COLOR_HEX, nColor))); + final ColorScalerDialog dlg = new ColorScalerDialog(getContext(), nColor); + dlg.setOnColorChangedListener(new OnColorChangedListener() { @Override - public void beforeColorChanged() { - } + public void beforeColorChanged() {} @Override public void onColorChanged(int color) { @@ -699,49 +685,49 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener, } @Override - public void afterColorChanged() { - - } - - + public void afterColorChanged() {} }); - dlg.show(); - } + dlg.show(); + } + // ====================== 内部类 ====================== class ColorScalerDialog extends ColorPickerDialog { - public int currentColorScalerDialogColor = 0; - public ColorScalerDialog(Context context, int p) { - super(context, p); - } + public int currentColorScalerDialogColor = 0; - @Override - public void dismiss() { - super.dismiss(); - int color = currentColorScalerDialogColor; - ToastUtils.show(String.format("dismiss color %d", color)); - if (!isAppSelfUpdatingColor) { - isAppSelfUpdatingColor = true; - try { - mOriginalAlpha = Color.alpha(color); - mOriginalAlphaPercent = alpha2Percent(mOriginalAlpha); - mOriginalR = Color.red(color); - mOriginalG = Color.green(color); - mOriginalB = Color.blue(color); - mCurrentAlpha = mOriginalAlpha; - mCurrentAlphaPercent = mOriginalAlphaPercent; - mCurrentR = mOriginalR; - mCurrentG = mOriginalG; - mCurrentB = mOriginalB; - mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; - mCurrentColor = color; - updateAllViews(); - LogUtils.d(TAG, "select system color | 选择颜色:" + String.format("#%08X", color) - + " | 透明度:" + mCurrentAlphaPercent + "%"); - } finally { - isAppSelfUpdatingColor = false; - } - } - } - } + public ColorScalerDialog(Context context, int p) { + super(context, p); + this.currentColorScalerDialogColor = p; + } + + @Override + public void dismiss() { + super.dismiss(); + int color = currentColorScalerDialogColor; + ToastUtils.show(String.format("选择颜色:%s", String.format(FORMAT_COLOR_HEX, color))); + if (!isAppSelfUpdatingColor) { + isAppSelfUpdatingColor = true; + try { + mOriginalAlpha = Color.alpha(color); + mOriginalAlphaPercent = alpha2Percent(mOriginalAlpha); + mOriginalR = Color.red(color); + mOriginalG = Color.green(color); + mOriginalB = Color.blue(color); + mCurrentAlpha = mOriginalAlpha; + mCurrentAlphaPercent = mOriginalAlphaPercent; + mCurrentR = mOriginalR; + mCurrentG = mOriginalG; + mCurrentB = mOriginalB; + mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; + mCurrentColor = color; + updateAllViews(); + LogUtils.d(TAG, String.format("select scaler color | 选择颜色:%s | 透明度:%s", + String.format(FORMAT_COLOR_HEX, color), + String.format(FORMAT_PERCENT, mCurrentAlphaPercent))); + } finally { + isAppSelfUpdatingColor = false; + } + } + } + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java index 0f94bb9..7703abe 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java @@ -1,10 +1,10 @@ package cc.winboll.studio.powerbell.dialogs; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.os.Handler; import android.os.Message; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; @@ -15,69 +15,82 @@ import androidx.appcompat.app.AlertDialog; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.R; +import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; +import cc.winboll.studio.powerbell.utils.ImageDownloader; import cc.winboll.studio.powerbell.views.BackgroundView; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import cc.winboll.studio.powerbell.utils.ImageDownloader; -import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; -import android.text.TextUtils; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/11/19 20:11 - * @Describe 网络后台使用提示对话框 + * @Describe 网络背景使用提示对话框 * 继承 AndroidX AlertDialog,绑定自定义布局 dialog_networkbackground.xml + * 适配 API30,基于 Java7 开发,支持网络图片下载、预览与回调 */ public class NetworkBackgroundDialog extends AlertDialog { + // ====================== 静态常量(首屏可见,统一管理) ====================== public static final String TAG = "NetworkBackgroundDialog"; - // 消息标识:图片加载成功 - private static final int MSG_IMAGE_LOAD_SUCCESS = 1001; - // 消息标识:图片加载失败 - private static final int MSG_IMAGE_LOAD_FAILED = 1002; + private static final int MSG_IMAGE_LOAD_SUCCESS = 1001; // 图片加载成功消息标识 + private static final int MSG_IMAGE_LOAD_FAILED = 1002; // 图片加载失败消息标识 - // 控件引用 - private TextView tvTitle; - private TextView tvContent; - private Button btnCancel; - private Button btnConfirm; - private Button btnPreview; - private EditText etURL; - BackgroundView mBackgroundView; - Context mContext; - // 主线程 Handler,用于接收子线程消息并更新 UI - private Handler mUiHandler; - String mPreviewFilePath; - String mPreviewFileUrl; - String mDownloadSavedPath; - - // 按钮点击回调接口(Java7 接口实现) + // ====================== 回调接口(紧跟常量,逻辑关联) ====================== + /** + * 按钮点击回调接口(Java7 接口实现) + */ public interface OnDialogClickListener { - void onConfirm(String szConfirmFilePath); // 确认按钮点击 - void onCancel(); // 取消按钮点击 + void onConfirm(String szConfirmFilePath); // 确认按钮点击,返回图片路径 + void onCancel(); // 取消按钮点击 } - private OnDialogClickListener listener; + // ====================== 成员变量(按优先级排序:核心数据→控件引用) ====================== + // 核心数据 + private OnDialogClickListener listener; // 按钮点击回调 + private Context mContext; // 上下文对象 + private Handler mUiHandler; // 主线程 Handler,用于接收子线程消息更新 UI + private String mPreviewFilePath; // 预览图片文件路径 + private String mPreviewFileUrl; // 预览图片网络 URL + private String mDownloadSavedPath; // 下载图片保存路径 + // 控件引用 + private TextView tvTitle; // 对话框标题 + private TextView tvContent; // 对话框内容 + private Button btnCancel; // 取消按钮 + private Button btnConfirm; // 确认按钮 + private Button btnPreview; // 预览按钮 + private EditText etURL; // URL 输入框 + private BackgroundView mBackgroundView; // 背景预览视图 - // Java7 显式构造(必须传入 Context) + // ====================== 构造方法(Java7 显式构造,按参数重载排序) ====================== + /** + * 基础构造(仅传入 Context) + * @param context 上下文 + */ public NetworkBackgroundDialog(@NonNull Context context) { super(context); - initHandler(); // 初始化 Handler - initView(); // 初始化布局和控件 - setDismissListener(); // 设置对话框消失监听 - } - - // 带回调的构造(便于外部处理点击事件) - public NetworkBackgroundDialog(@NonNull Context context, OnDialogClickListener listener) { - super(context); - this.listener = listener; - initHandler(); // 初始化 Handler + LogUtils.d(TAG, "NetworkBackgroundDialog: 基础构造初始化"); + initHandler(); initView(); - setDismissListener(); // 设置对话框消失监听 + setDismissListener(); } /** - * 初始化主线程 Handler,用于更新 UI + * 带回调的构造(便于外部处理点击事件) + * @param context 上下文 + * @param listener 按钮点击回调 + */ + public NetworkBackgroundDialog(@NonNull Context context, OnDialogClickListener listener) { + super(context); + this.listener = listener; + LogUtils.d(TAG, "NetworkBackgroundDialog: 带回调构造初始化"); + initHandler(); + initView(); + setDismissListener(); + } + + // ====================== 生命周期相关方法(对话框消失监听、Handler 初始化) ====================== + /** + * 初始化主线程 Handler,用于接收子线程消息并更新 UI */ private void initHandler() { mUiHandler = new Handler() { @@ -86,22 +99,28 @@ public class NetworkBackgroundDialog extends AlertDialog { super.handleMessage(msg); // 对话框已消失时,不再处理 UI 消息 if (!isShowing()) { + LogUtils.d(TAG, "handleMessage: 对话框已消失,忽略消息"); return; } switch (msg.what) { case MSG_IMAGE_LOAD_SUCCESS: // 图片加载成功,获取文件路径并设置背景 mDownloadSavedPath = (String) msg.obj; - mBackgroundView.loadImage(mDownloadSavedPath); + LogUtils.d(TAG, String.format("handleMessage: 图片加载成功,保存路径:%s", mDownloadSavedPath)); + mBackgroundView.loadImage(mDownloadSavedPath); break; case MSG_IMAGE_LOAD_FAILED: // 图片加载失败,设置默认背景 + LogUtils.e(TAG, "handleMessage: 图片加载失败"); mBackgroundView.setBackgroundResource(R.drawable.ic_launcher); ToastUtils.show("图片预览失败,请检查链接"); break; + default: + break; } } }; + LogUtils.d(TAG, "initHandler: 主线程 Handler 初始化完成"); } /** @@ -114,20 +133,22 @@ public class NetworkBackgroundDialog extends AlertDialog { // 对话框消失时,移除所有未处理的消息和回调 if (mUiHandler != null) { mUiHandler.removeCallbacksAndMessages(null); + LogUtils.d(TAG, "onDismiss: Handler 消息已清理"); } - LogUtils.d(TAG, "对话框已消失,Handler 消息已清理"); + LogUtils.d(TAG, "onDismiss: 对话框已消失"); } }); + LogUtils.d(TAG, "setDismissListener: 对话框消失监听已设置"); } + // ====================== 初始化方法(布局、控件、点击事件) ====================== /** * 初始化布局和控件 */ private void initView() { mContext = this.getContext(); // 加载自定义布局 - View dialogView = LayoutInflater.from(getContext()) - .inflate(R.layout.dialog_networkbackground, null); + View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_networkbackground, null); // 设置对话框内容视图 setView(dialogView); @@ -139,10 +160,21 @@ public class NetworkBackgroundDialog extends AlertDialog { btnPreview = (Button) dialogView.findViewById(R.id.btn_preview); etURL = (EditText) dialogView.findViewById(R.id.et_url); mBackgroundView = (BackgroundView) dialogView.findViewById(R.id.bv_background_preview); - // 加载初始图片 - mBackgroundView.setBackgroundResource(R.drawable.blank100x100); + + // 控件非空校验 + if (tvTitle == null || tvContent == null || btnCancel == null || btnConfirm == null || btnPreview == null + || etURL == null || mBackgroundView == null) { + LogUtils.e(TAG, "initView: 控件绑定失败,请检查布局ID是否正确"); + dismiss(); + return; + } + + // 加载初始图片 + mBackgroundView.setBackgroundResource(R.drawable.blank100x100); // 设置按钮点击事件 setButtonClickListeners(); + + LogUtils.d(TAG, "initView: 布局和控件初始化完成"); } /** @@ -153,13 +185,14 @@ public class NetworkBackgroundDialog extends AlertDialog { btnCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LogUtils.d("NetworkBackgroundDialog", "取消按钮点击"); + LogUtils.d(TAG, "onClick: 取消按钮点击"); BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext); utils.setCurrentSourceToPreview(); dismiss(); // 关闭对话框 if (listener != null) { listener.onCancel(); + LogUtils.d(TAG, "onClick: 取消回调已执行"); } } }); @@ -168,14 +201,16 @@ public class NetworkBackgroundDialog extends AlertDialog { btnConfirm.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LogUtils.d("NetworkBackgroundDialog", "确认按钮点击"); + LogUtils.d(TAG, "onClick: 确认按钮点击"); dismiss(); // 关闭对话框 - if(TextUtils.isEmpty(mDownloadSavedPath)) { + if (TextUtils.isEmpty(mDownloadSavedPath)) { ToastUtils.show("未下载图片。"); + LogUtils.w(TAG, "onClick: 确认失败,未下载图片"); return; } if (listener != null) { listener.onConfirm(mDownloadSavedPath); + LogUtils.d(TAG, String.format("onClick: 确认回调已执行,图片路径:%s", mDownloadSavedPath)); } } }); @@ -184,90 +219,120 @@ public class NetworkBackgroundDialog extends AlertDialog { btnPreview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + LogUtils.d(TAG, "onClick: 预览按钮点击"); downloadImageToAlbumAndPreview(); } }); + + LogUtils.d(TAG, "setButtonClickListeners: 按钮点击监听已设置"); + } + + // ====================== 业务逻辑方法(图片下载、预览) ====================== + /** + * 下载网络图片并预览 + */ + void downloadImageToAlbumAndPreview() { + mPreviewFileUrl = etURL.getText().toString().trim(); + if (TextUtils.isEmpty(mPreviewFileUrl)) { + ToastUtils.show("请输入图片URL"); + LogUtils.w(TAG, "downloadImageToAlbumAndPreview: 图片URL为空"); + return; + } + + LogUtils.d(TAG, String.format("downloadImageToAlbumAndPreview: 开始下载图片,URL:%s", mPreviewFileUrl)); + ImageDownloader.getInstance(mContext).downloadImage(mPreviewFileUrl, new ImageDownloader.DownloadCallback() { + @Override + public void onSuccess(String savePath) { + LogUtils.d(TAG, String.format("onSuccess: 图片下载成功,保存路径:%s", savePath)); + // 发送消息到主线程,携带图片路径 + Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, savePath); + mUiHandler.sendMessage(successMsg); + } + + @Override + public void onFailure(String errorMsg) { + LogUtils.e(TAG, String.format("onFailure: 图片下载失败,错误信息:%s", errorMsg)); + ToastUtils.show("下载失败:" + errorMsg); + // 发送图片加载失败消息 + Message failMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_FAILED); + mUiHandler.sendMessage(failMsg); + } + }); } /** * 根据文件路径设置 BackgroundView 背景(主线程调用) - * @param filePath 图片文件路径 + * @param previewFilePath 图片文件路径 */ private void previewBackground(String previewFilePath) { + if (TextUtils.isEmpty(previewFilePath)) { + LogUtils.w(TAG, "previewBackground: 预览文件路径为空"); + return; + } + FileInputStream fis = null; try { File imageFile = new File(previewFilePath); if (!imageFile.exists()) { - ToastUtils.show("图片文件不存在:" + previewFilePath); - LogUtils.e(TAG, "图片文件不存在:" + previewFilePath); - mBackgroundView.setBackgroundResource(R.drawable.ic_launcher); + ToastUtils.show("图片文件不存在:" + previewFilePath); + LogUtils.e(TAG, String.format("previewBackground: 图片文件不存在,路径:%s", previewFilePath)); + mBackgroundView.setBackgroundResource(R.drawable.ic_launcher); return; } - // 预览背景 - mPreviewFilePath = previewFilePath; - BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext); - utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl); - mBackgroundView.loadByBackgroundBean(utils.getPreviewBackgroundBean()); + // 预览背景 + mPreviewFilePath = previewFilePath; + BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext); + utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl); + mBackgroundView.loadByBackgroundBean(utils.getPreviewBackgroundBean()); + + LogUtils.d(TAG, String.format("previewBackground: 图片预览成功,路径:%s", previewFilePath)); } catch (Exception e) { - e.printStackTrace(); + LogUtils.e(TAG, String.format("previewBackground: 图片预览失败,错误信息:%s", e.getMessage()), e); mBackgroundView.setBackgroundResource(R.drawable.ic_launcher); - LogUtils.e(TAG, "图片预览失败:" + e.getMessage()); } finally { // Java7 手动关闭流,避免资源泄漏 if (fis != null) { try { fis.close(); + LogUtils.d(TAG, "previewBackground: 文件输入流已关闭"); } catch (IOException e) { - e.printStackTrace(); + LogUtils.e(TAG, String.format("previewBackground: 关闭文件输入流失败,错误信息:%s", e.getMessage()), e); } } } } + // ====================== 对外提供方法(灵活适配不同场景) ====================== /** - * 对外提供方法:修改对话框标题(灵活适配不同场景) + * 对外提供方法:修改对话框标题 + * @param title 标题文本 */ public void setTitle(String title) { - if (tvTitle != null) { + if (tvTitle != null && !TextUtils.isEmpty(title)) { tvTitle.setText(title); + LogUtils.d(TAG, String.format("setTitle: 对话框标题已修改为:%s", title)); } } /** - * 对外提供方法:修改对话框内容(灵活适配不同场景) + * 对外提供方法:修改对话框内容 + * @param content 内容文本 */ public void setContent(String content) { - if (tvContent != null) { + if (tvContent != null && !TextUtils.isEmpty(content)) { tvContent.setText(content); + LogUtils.d(TAG, String.format("setContent: 对话框内容已修改为:%s", content)); } } /** * 对外提供方法:设置按钮点击回调(替代带参构造) + * @param listener 按钮点击回调 */ public void setOnDialogClickListener(OnDialogClickListener listener) { this.listener = listener; + LogUtils.d(TAG, "setOnDialogClickListener: 按钮点击回调已设置"); } - - void downloadImageToAlbumAndPreview() { - //String previewFileUrl = "https://example.com/test.jpg"; - mPreviewFileUrl = etURL.getText().toString(); - ImageDownloader.getInstance(mContext).downloadImage(mPreviewFileUrl, new ImageDownloader.DownloadCallback(){ - @Override - public void onSuccess(String savePath) { - // 发送消息到主线程,携带图片路径 - Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, savePath); - mUiHandler.sendMessage(successMsg); - - } - - @Override - public void onFailure(String errorMsg) { - ToastUtils.show("下载失败:" + errorMsg); - } - }); - - } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/YesNoAlertDialog.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/YesNoAlertDialog.java deleted file mode 100644 index 6018f5f..0000000 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/YesNoAlertDialog.java +++ /dev/null @@ -1,59 +0,0 @@ -package cc.winboll.studio.powerbell.dialogs; - -/** - * @Author ZhanGSKen - * @Date 2024/06/10 19:32:55 - * @Describe 用户确定与否选择框 - */ -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; - -public class YesNoAlertDialog { - - public static final String TAG = "YesNoAlertDialog"; - - public static void show(Context context, String szTitle, String szMessage, final OnDialogResultListener listener) { - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder( - context); - - // set title - alertDialogBuilder.setTitle(szTitle); - - // set dialog message - alertDialogBuilder - .setMessage(szMessage) - .setCancelable(true) - .setOnCancelListener(new DialogInterface.OnCancelListener(){ - @Override - public void onCancel(DialogInterface dialog) { - listener.onNo(); - } - }) - .setPositiveButton("YES", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - // if this button is clicked, close - // current activity - listener.onYes(); - } - }) - .setNegativeButton("NO", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - // if this button is clicked, just close - // the dialog box and do nothing - dialog.cancel(); - } - }); - - // create alert dialog - AlertDialog alertDialog = alertDialogBuilder.create(); - - // show it - alertDialog.show(); - } - - public interface OnDialogResultListener { - abstract void onYes(); - abstract void onNo(); - } -} diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/AppConfigBean.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/AppConfigBean.java index ae8190f..6cf1c0c 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/AppConfigBean.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/AppConfigBean.java @@ -1,127 +1,175 @@ package cc.winboll.studio.powerbell.models; -/** - * @Author ZhanGSKen - * @Date 2024/04/29 17:24:53 - * @Describe 应用运行参数类 - */ import android.os.Parcel; import android.os.Parcelable; import android.util.JsonReader; import android.util.JsonWriter; import cc.winboll.studio.libappbase.BaseBean; +import cc.winboll.studio.libappbase.LogUtils; import java.io.IOException; import java.io.Serializable; -// 核心修正:新增 Parcelable 接口实现(API30 持久化/Intent 传递必备) +/** + * @Author ZhanGSKen + * @Date 2024/04/29 17:24:53 + * @Describe 应用运行参数类 + * 适配 API30,支持 Serializable 持久化、Parcelable Intent 传递、JSON 序列化/反序列化 + * 包含耗电提醒、充电提醒、电量检测、铃声提醒等核心配置 + */ public class AppConfigBean extends BaseBean implements Serializable, Parcelable { - + // ====================== 静态常量(首屏可见,统一管理) ====================== // 序列化版本号(Serializable 必备,避免反序列化失败) private static final long serialVersionUID = 1L; - + // 日志标签(全局统一,替换 Log 为 LogUtils) transient public static final String TAG = "AppConfigBean"; + // 字段校验常量(统一阈值,避免硬编码) + private static final int MIN_INTERVAL = 500; // 最小检测间隔(ms) + private static final int MIN_REMIND_INTERVAL = 1000; // 最小提醒间隔(ms) + private static final int BATTERY_MIN = 0; // 电量最小值 + private static final int BATTERY_MAX = 100; // 电量最大值 + private static final int INVALID_BATTERY = -1; // 无效电量标识 - // 核心配置字段(保留原有字段,统一状态字段命名) + // ====================== 成员变量(按功能分类:提醒配置→电量状态→检测配置) ====================== + // 耗电提醒配置 boolean isEnableUsageReminder = false; // 耗电提醒开关 int usageReminderValue = 45; // 耗电提醒阈值(0-100) + // 充电提醒配置 boolean isEnableChargeReminder = false;// 充电提醒开关 int chargeReminderValue = 100; // 充电提醒阈值(0-100) - int reminderIntervalTime = 5000; // 铃声提醒间隔(ms,原有) - boolean isCharging = false; // 是否充电(状态字段,原有) - int currentBatteryValue = -1; // 修正:统一命名为「currentBatteryValue」(原 currentValue) - int batteryDetectInterval = 2000; // 新增:电量检测间隔(ms,适配 RemindThread) + // 铃声提醒配置 + int reminderIntervalTime = 5000; // 铃声提醒间隔(ms) + // 电量状态 + boolean isCharging = false; // 是否充电 + int currentBatteryValue = INVALID_BATTERY; // 当前电池电量(统一命名,替代原 currentValue) + // 电量检测配置 + int batteryDetectInterval = 2000; // 电量检测间隔(ms,适配 RemindThread) - // 构造方法:初始化默认配置(同步修正字段名,统一默认值) + // ====================== 构造方法(初始化默认配置,强化默认值校验) ====================== public AppConfigBean() { setChargeReminderValue(100); setEnableChargeReminder(false); setUsageReminderValue(10); setEnableUsageReminder(false); setReminderIntervalTime(5000); - setBatteryDetectInterval(1000); // 新增:默认检测间隔1秒 - setCurrentBatteryValue(-1); // 修正:初始化当前电量字段 + setBatteryDetectInterval(1000); // 默认检测间隔1秒 + setCurrentBatteryValue(INVALID_BATTERY); // 初始化无效电量标识 + LogUtils.d(TAG, "AppConfigBean: 初始化默认配置完成"); } - // ====================== 核心修复:补全缺失方法(适配 RemindThread/Receiver 调用) ====================== + // ====================== 核心业务方法(Setter/Getter,按字段功能分类,补充调试日志) ====================== + // --------------- 电量状态相关 --------------- /** - * 设置当前电池电量(Receiver 监听电池变化时调用,与 RemindThread 字段对齐) + * 设置当前电池电量(Receiver 监听电池变化时调用) + * @param currentBatteryValue 当前电量(0-100) */ public void setCurrentBatteryValue(int currentBatteryValue) { - // 强化校验:电量范围限制在 0-100,异常值置为 -1(标识无效) - this.currentBatteryValue = (currentBatteryValue >= 0 && currentBatteryValue <= 100) - ? currentBatteryValue : -1; + this.currentBatteryValue = (currentBatteryValue >= BATTERY_MIN && currentBatteryValue <= BATTERY_MAX) + ? currentBatteryValue : INVALID_BATTERY; + LogUtils.d(TAG, String.format("setCurrentBatteryValue: 当前电量设置为 %d(输入值:%d)", + this.currentBatteryValue, currentBatteryValue)); } /** - * 获取当前电池电量(RemindThread 同步配置时调用,与 set 方法对应) + * 获取当前电池电量(RemindThread 同步配置时调用) + * @return 当前电量(0-100 或 INVALID_BATTERY) */ public int getCurrentBatteryValue() { return currentBatteryValue; } - // ====================== 原有字段 Setter/Getter(修正命名,强化校验) ====================== + // --------------- 铃声提醒配置相关 --------------- + /** + * 设置铃声提醒间隔 + * @param reminderIntervalTime 提醒间隔(ms,不小于 MIN_REMIND_INTERVAL) + */ public void setReminderIntervalTime(int reminderIntervalTime) { - // 校验:提醒间隔不小于 1000ms,避免频繁提醒 - this.reminderIntervalTime = Math.max(reminderIntervalTime, 1000); + this.reminderIntervalTime = Math.max(reminderIntervalTime, MIN_REMIND_INTERVAL); + LogUtils.d(TAG, String.format("setReminderIntervalTime: 提醒间隔设置为 %dms(输入值:%d)", + this.reminderIntervalTime, reminderIntervalTime)); } public int getReminderIntervalTime() { return reminderIntervalTime; } - public void setIsCharging(boolean isCharging) { // 修正:方法名与字段名统一(原 setCharging) + // --------------- 充电状态相关 --------------- + /** + * 设置是否充电 + * @param isCharging 充电状态 + */ + public void setIsCharging(boolean isCharging) { this.isCharging = isCharging; + LogUtils.d(TAG, String.format("setIsCharging: 充电状态设置为 %b", isCharging)); } public boolean isCharging() { return isCharging; } + // --------------- 耗电提醒配置相关 --------------- public void setEnableUsageReminder(boolean isEnableUsageReminder) { this.isEnableUsageReminder = isEnableUsageReminder; + LogUtils.d(TAG, String.format("setEnableUsageReminder: 耗电提醒开关设置为 %b", isEnableUsageReminder)); } public boolean isEnableUsageReminder() { return isEnableUsageReminder; } + /** + * 设置耗电提醒阈值 + * @param usageReminderValue 阈值(0-100) + */ public void setUsageReminderValue(int usageReminderValue) { - // 校验:阈值范围 0-100 - this.usageReminderValue = Math.min(Math.max(usageReminderValue, 0), 100); + this.usageReminderValue = Math.min(Math.max(usageReminderValue, BATTERY_MIN), BATTERY_MAX); + LogUtils.d(TAG, String.format("setUsageReminderValue: 耗电提醒阈值设置为 %d(输入值:%d)", + this.usageReminderValue, usageReminderValue)); } public int getUsageReminderValue() { return usageReminderValue; } + // --------------- 充电提醒配置相关 --------------- public void setEnableChargeReminder(boolean isEnableChargeReminder) { this.isEnableChargeReminder = isEnableChargeReminder; + LogUtils.d(TAG, String.format("setEnableChargeReminder: 充电提醒开关设置为 %b", isEnableChargeReminder)); } public boolean isEnableChargeReminder() { return isEnableChargeReminder; } + /** + * 设置充电提醒阈值 + * @param chargeReminderValue 阈值(0-100) + */ public void setChargeReminderValue(int chargeReminderValue) { - // 校验:阈值范围 0-100 - this.chargeReminderValue = Math.min(Math.max(chargeReminderValue, 0), 100); + this.chargeReminderValue = Math.min(Math.max(chargeReminderValue, BATTERY_MIN), BATTERY_MAX); + LogUtils.d(TAG, String.format("setChargeReminderValue: 充电提醒阈值设置为 %d(输入值:%d)", + this.chargeReminderValue, chargeReminderValue)); } public int getChargeReminderValue() { return chargeReminderValue; } - // ====================== 电量检测间隔 Setter/Getter(适配 RemindThread) ====================== + // --------------- 电量检测配置相关 --------------- + /** + * 设置电量检测间隔 + * @param batteryDetectInterval 检测间隔(ms,不小于 MIN_INTERVAL) + */ + public void setBatteryDetectInterval(int batteryDetectInterval) { + this.batteryDetectInterval = Math.max(batteryDetectInterval, MIN_INTERVAL); + LogUtils.d(TAG, String.format("setBatteryDetectInterval: 检测间隔设置为 %dms(输入值:%d)", + this.batteryDetectInterval, batteryDetectInterval)); + } + public int getBatteryDetectInterval() { return batteryDetectInterval; } - // 强化校验:检测间隔不小于500ms(避免 CPU 高占用,与 RemindThread 最小休眠一致) - public void setBatteryDetectInterval(int batteryDetectInterval) { - this.batteryDetectInterval = Math.max(batteryDetectInterval, 500); - } - - // ====================== JSON 序列化/反序列化(兼容旧配置,同步修正字段) ====================== + // ====================== JSON 序列化/反序列化(兼容旧配置,补充调试日志) ====================== @Override public String getName() { return AppConfigBean.class.getName(); @@ -131,18 +179,19 @@ public class AppConfigBean extends BaseBean implements Serializable, Parcelable public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { super.writeThisToJsonWriter(jsonWriter); AppConfigBean bean = this; - // 原有字段序列化(保留拼写兼容,同步修正字段名) + // 原有字段序列化 jsonWriter.name("isEnableUsageReminder").value(bean.isEnableUsageReminder()); jsonWriter.name("usageReminderValue").value(bean.getUsageReminderValue()); jsonWriter.name("isEnableChargeReminder").value(bean.isEnableChargeReminder()); jsonWriter.name("chargeReminderValue").value(bean.getChargeReminderValue()); jsonWriter.name("reminderIntervalTime").value(bean.getReminderIntervalTime()); jsonWriter.name("isCharging").value(bean.isCharging()); - // 修正:序列化新字段名 currentBatteryValue,兼容旧字段 currentValue + // 兼容旧字段 currentValue,同步新字段 currentBatteryValue jsonWriter.name("currentBatteryValue").value(bean.getCurrentBatteryValue()); - jsonWriter.name("currentValue").value(bean.getCurrentBatteryValue()); // 兼容旧配置,避免数据丢失 - // 新增字段序列化:电量检测间隔 + jsonWriter.name("currentValue").value(bean.getCurrentBatteryValue()); + // 新增字段序列化 jsonWriter.name("batteryDetectInterval").value(bean.getBatteryDetectInterval()); + LogUtils.d(TAG, "writeThisToJsonWriter: JSON 序列化完成"); } @Override @@ -151,7 +200,7 @@ public class AppConfigBean extends BaseBean implements Serializable, Parcelable jsonReader.beginObject(); while (jsonReader.hasNext()) { String name = jsonReader.nextName(); - // 原有字段反序列化(兼容旧 Key 拼写,同步修正字段) + // 兼容拼写错误字段(isEnableUsegeReminder → isEnableUsageReminder) if (name.equals("isEnableUsageReminder") || name.equals("isEnableUsegeReminder")) { bean.setEnableUsageReminder(jsonReader.nextBoolean()); } else if (name.equals("usageReminderValue") || name.equals("usegeReminderValue")) { @@ -163,58 +212,62 @@ public class AppConfigBean extends BaseBean implements Serializable, Parcelable } else if (name.equals("reminderIntervalTime")) { bean.setReminderIntervalTime(jsonReader.nextInt()); } else if (name.equals("isCharging")) { - bean.setIsCharging(jsonReader.nextBoolean()); // 修正:调用新方法名 - } - // 核心兼容:优先读取旧字段 currentValue,再读取新字段 currentBatteryValue(新字段覆盖旧字段) - else if (name.equals("currentValue")) { + bean.setIsCharging(jsonReader.nextBoolean()); + } else if (name.equals("currentValue")) { + // 优先读取旧字段,兼容历史配置 bean.setCurrentBatteryValue(jsonReader.nextInt()); + LogUtils.d(TAG, "readBeanFromJsonReader: 读取旧字段 currentValue 完成"); } else if (name.equals("currentBatteryValue")) { + // 新字段覆盖旧字段,保证数据最新 bean.setCurrentBatteryValue(jsonReader.nextInt()); - } - // 新增字段反序列化(兼容无此字段的旧配置,用默认值1000ms) - else if (name.equals("batteryDetectInterval")) { + LogUtils.d(TAG, "readBeanFromJsonReader: 读取新字段 currentBatteryValue 完成"); + } else if (name.equals("batteryDetectInterval")) { bean.setBatteryDetectInterval(jsonReader.nextInt()); } else { jsonReader.skipValue(); + LogUtils.w(TAG, String.format("readBeanFromJsonReader: 跳过未知字段 %s", name)); } } jsonReader.endObject(); + LogUtils.d(TAG, "readBeanFromJsonReader: JSON 反序列化完成"); return bean; } - // ====================== Parcelable 接口实现(同步修正字段,确保 Intent 传递正常) ====================== + // ====================== Parcelable 接口实现(API30 Intent 传递必备,补充调试日志) ====================== @Override public int describeContents() { return 0; // 无特殊内容描述,固定返回0 } - // 序列化:将所有字段写入 Parcel(同步修正字段名,Java7 适配) @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeByte((byte) (isEnableUsageReminder ? 1 : 0)); // boolean → byte + // 按成员变量顺序写入,boolean 转 byte 存储 + dest.writeByte((byte) (isEnableUsageReminder ? 1 : 0)); dest.writeInt(usageReminderValue); - dest.writeByte((byte) (isEnableChargeReminder ? 1 : 0)); // boolean → byte + dest.writeByte((byte) (isEnableChargeReminder ? 1 : 0)); dest.writeInt(chargeReminderValue); dest.writeInt(reminderIntervalTime); - dest.writeByte((byte) (isCharging ? 1 : 0)); // boolean → byte - dest.writeInt(currentBatteryValue); // 修正:序列化新字段名 + dest.writeByte((byte) (isCharging ? 1 : 0)); + dest.writeInt(currentBatteryValue); dest.writeInt(batteryDetectInterval); + LogUtils.d(TAG, "writeToParcel: Parcel 序列化完成"); } - // 反序列化:从 Parcel 读取字段,创建对象(必须 public static final 修饰) + // 反序列化 Creator(必须 public static final 修饰,Java7 适配) public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public AppConfigBean createFromParcel(Parcel source) { AppConfigBean bean = new AppConfigBean(); - // 按 writeToParcel 顺序读取,同步修正字段 + // 按 writeToParcel 顺序读取 bean.isEnableUsageReminder = source.readByte() != 0; bean.usageReminderValue = source.readInt(); bean.isEnableChargeReminder = source.readByte() != 0; bean.chargeReminderValue = source.readInt(); bean.reminderIntervalTime = source.readInt(); bean.isCharging = source.readByte() != 0; - bean.currentBatteryValue = source.readInt(); // 修正:读取新字段名 + bean.currentBatteryValue = source.readInt(); bean.batteryDetectInterval = source.readInt(); + LogUtils.d(TAG, "createFromParcel: Parcel 反序列化完成"); return bean; } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BackgroundBean.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BackgroundBean.java index 57a5e42..630b148 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BackgroundBean.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BackgroundBean.java @@ -3,51 +3,61 @@ package cc.winboll.studio.powerbell.models; import android.util.JsonReader; import android.util.JsonWriter; import cc.winboll.studio.libappbase.BaseBean; +import cc.winboll.studio.libappbase.LogUtils; import java.io.IOException; import java.io.Serializable; /** * @Author ZhanGSKen * @Date 2024/07/18 11:52:28 - * @Describe 应用背景图片数据类(存储正式/预览背景配置,支持JSON序列化/反序列化) + * @Describe 应用背景图片数据类 + * 适配 API30,支持 Serializable 持久化、JSON 序列化/反序列化 + * 存储正式/预览背景配置,包含原图、压缩图、裁剪比例、像素颜色等核心字段 */ public class BackgroundBean extends BaseBean implements Serializable { + // ====================== 静态常量(首屏可见,统一管理) ====================== + // 日志标签(全局统一,替换 Log 为 LogUtils) + public static final String TAG = "BackgroundBean"; + // 兼容旧字段常量(统一管理,避免硬编码) + private static final String OLD_FIELD_USE_SCALED_COMPRESS = "isUseScaledCompress"; + // 字段默认值常量(统一管理,避免魔法值) + private static final int DEFAULT_DIMENSION = 100; // 默认宽高 + private static final int MIN_DIMENSION = 1; // 最小宽高 - public static final String TAG = "BackgroundPictureBean"; - - // 核心字段:背景图片文件名(对应应用私有目录下的图片文件,与BackgroundSettingsActivity的_mSourceCroppedFile匹配) - private String backgroundFileName = ""; - // 核心字段:背景图片完整路径(解决仅存文件名导致的路径拼接错误,与backgroundScaledCompressFilePath对应) - private String backgroundFilePath = ""; - // 附加字段:图片信息(如Uri、网络地址等,仅作备注,不参与路径生成) - private String backgroundFileInfo = ""; - // 控制字段:是否启用背景图片(true-显示背景图,false-显示透明背景) - private boolean isUseBackgroundFile = false; - // 核心字段:压缩后背景图片文件名(对应应用私有目录下的压缩图片,与saveCropBitmap的压缩图匹配) - private String backgroundScaledCompressFileName = ""; - // 核心字段:压缩后背景图片完整路径(解决仅存文件名导致的路径拼接错误,适配BackgroundSettingsActivity的私有目录) - private String backgroundScaledCompressFilePath = ""; - // 重命名字段:是否启用压缩背景图(原isUseScaledCompress → 新isUseBackgroundScaledCompressFile,语义更清晰) - private boolean isUseBackgroundScaledCompressFile = false; - // 裁剪比例字段:背景图宽高比(默认1:1,用于固定比例裁剪) - private int backgroundWidth = 100; - private int backgroundHeight = 100; - // 像素拾取字段:拾取的像素颜色(用于纯色背景) - private int pixelColor = 0; + // ====================== 成员变量(按功能分类:原图配置→压缩图配置→控制字段→裁剪配置→像素颜色) ====================== + // 原图配置 + private String backgroundFileName = ""; // 背景图片文件名 + private String backgroundFilePath = ""; // 背景图片完整路径 + private String backgroundFileInfo = ""; // 图片信息(Uri、网络地址等) + // 压缩图配置 + private String backgroundScaledCompressFileName = ""; // 压缩后背景图片文件名 + private String backgroundScaledCompressFilePath = ""; // 压缩后背景图片完整路径 + // 控制字段 + private boolean isUseBackgroundFile = false; // 是否启用背景图片 + private boolean isUseBackgroundScaledCompressFile = false; // 是否启用压缩背景图(重命名:原isUseScaledCompress) + // 裁剪配置 + private int backgroundWidth = DEFAULT_DIMENSION; // 背景图宽度 + private int backgroundHeight = DEFAULT_DIMENSION; // 背景图高度 + // 像素颜色 + private int pixelColor = 0; // 拾取的像素颜色(纯色背景用) + // ====================== 构造方法(无参构造,JSON反序列化必备) ====================== /** * 无参构造器(必须,JSON反序列化时需默认构造器) */ public BackgroundBean() { + LogUtils.d(TAG, "BackgroundBean: 无参构造初始化完成"); } - // ====================================== Getter/Setter 方法(全字段,含重命名+新增字段)====================================== + // ====================== Getter/Setter 方法(按功能分类,补充调试日志,强化校验) ====================== + // --------------- 原图配置相关 --------------- public String getBackgroundFileName() { return backgroundFileName; } public void setBackgroundFileName(String backgroundFileName) { - this.backgroundFileName = backgroundFileName == null ? "" : backgroundFileName; // 防null,避免空指针 + this.backgroundFileName = backgroundFileName == null ? "" : backgroundFileName; + LogUtils.d(TAG, String.format("setBackgroundFileName: 背景文件名设置为 %s", this.backgroundFileName)); } public String getBackgroundFilePath() { @@ -55,7 +65,8 @@ public class BackgroundBean extends BaseBean implements Serializable { } public void setBackgroundFilePath(String backgroundFilePath) { - this.backgroundFilePath = backgroundFilePath == null ? "" : backgroundFilePath; // 防null,避免路径拼接错误 + this.backgroundFilePath = backgroundFilePath == null ? "" : backgroundFilePath; + LogUtils.d(TAG, String.format("setBackgroundFilePath: 背景文件路径设置为 %s", this.backgroundFilePath)); } public String getBackgroundFileInfo() { @@ -63,23 +74,28 @@ public class BackgroundBean extends BaseBean implements Serializable { } public void setBackgroundFileInfo(String backgroundFileInfo) { - this.backgroundFileInfo = backgroundFileInfo == null ? "" : backgroundFileInfo; // 防null,避免空指针 + this.backgroundFileInfo = backgroundFileInfo == null ? "" : backgroundFileInfo; + LogUtils.d(TAG, String.format("setBackgroundFileInfo: 背景文件信息设置为 %s", this.backgroundFileInfo)); } + // --------------- 控制字段相关 --------------- public boolean isUseBackgroundFile() { return isUseBackgroundFile; } public void setIsUseBackgroundFile(boolean isUseBackgroundFile) { this.isUseBackgroundFile = isUseBackgroundFile; + LogUtils.d(TAG, String.format("setIsUseBackgroundFile: 是否启用背景图设置为 %b", isUseBackgroundFile)); } + // --------------- 压缩图配置相关 --------------- public String getBackgroundScaledCompressFileName() { return backgroundScaledCompressFileName; } public void setBackgroundScaledCompressFileName(String backgroundScaledCompressFileName) { - this.backgroundScaledCompressFileName = backgroundScaledCompressFileName == null ? "" : backgroundScaledCompressFileName; // 防null + this.backgroundScaledCompressFileName = backgroundScaledCompressFileName == null ? "" : backgroundScaledCompressFileName; + LogUtils.d(TAG, String.format("setBackgroundScaledCompressFileName: 压缩背景文件名设置为 %s", this.backgroundScaledCompressFileName)); } public String getBackgroundScaledCompressFilePath() { @@ -87,7 +103,8 @@ public class BackgroundBean extends BaseBean implements Serializable { } public void setBackgroundScaledCompressFilePath(String backgroundScaledCompressFilePath) { - this.backgroundScaledCompressFilePath = backgroundScaledCompressFilePath == null ? "" : backgroundScaledCompressFilePath; // 防null,避免路径错误 + this.backgroundScaledCompressFilePath = backgroundScaledCompressFilePath == null ? "" : backgroundScaledCompressFilePath; + LogUtils.d(TAG, String.format("setBackgroundScaledCompressFilePath: 压缩背景文件路径设置为 %s", this.backgroundScaledCompressFilePath)); } /** @@ -100,14 +117,17 @@ public class BackgroundBean extends BaseBean implements Serializable { public void setIsUseBackgroundScaledCompressFile(boolean isUseBackgroundScaledCompressFile) { this.isUseBackgroundScaledCompressFile = isUseBackgroundScaledCompressFile; + LogUtils.d(TAG, String.format("setIsUseBackgroundScaledCompressFile: 是否启用压缩背景图设置为 %b", isUseBackgroundScaledCompressFile)); } + // --------------- 裁剪配置相关 --------------- public int getBackgroundWidth() { return backgroundWidth; } public void setBackgroundWidth(int backgroundWidth) { - this.backgroundWidth = backgroundWidth <= 0 ? 100 : backgroundWidth; // 防无效值,确保宽高比有效 + this.backgroundWidth = backgroundWidth < MIN_DIMENSION ? DEFAULT_DIMENSION : backgroundWidth; + LogUtils.d(TAG, String.format("setBackgroundWidth: 背景宽度设置为 %d(输入值:%d)", this.backgroundWidth, backgroundWidth)); } public int getBackgroundHeight() { @@ -115,44 +135,54 @@ public class BackgroundBean extends BaseBean implements Serializable { } public void setBackgroundHeight(int backgroundHeight) { - this.backgroundHeight = backgroundHeight <= 0 ? 100 : backgroundHeight; // 防无效值,确保宽高比有效 + this.backgroundHeight = backgroundHeight < MIN_DIMENSION ? DEFAULT_DIMENSION : backgroundHeight; + LogUtils.d(TAG, String.format("setBackgroundHeight: 背景高度设置为 %d(输入值:%d)", this.backgroundHeight, backgroundHeight)); } + // --------------- 像素颜色相关 --------------- public int getPixelColor() { return pixelColor; } public void setPixelColor(int pixelColor) { this.pixelColor = pixelColor; + LogUtils.d(TAG, String.format("setPixelColor: 像素颜色设置为 0x%08X", pixelColor)); } - // ====================================== 序列化/反序列化方法(适配重命名字段,兼容旧版本)====================================== + // ====================== 序列化/反序列化方法(适配重命名字段,兼容旧版本,补充调试日志) ====================== @Override public String getName() { - return BackgroundBean.class.getName(); // 必须重写,BaseBean序列化时需类名标识 + String className = BackgroundBean.class.getName(); + LogUtils.d(TAG, String.format("getName: 类名标识为 %s", className)); + return className; } /** * 序列化:同步重命名字段(原isUseScaledCompress → 新isUseBackgroundScaledCompressFile) - * 确保新字段能正常持久化,同时兼容旧版本JSON(可选:保留旧字段写入,避免旧版本读取异常) + * 确保新字段能正常持久化,同时兼容旧版本JSON(保留旧字段写入,避免旧版本读取异常) */ @Override public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { super.writeThisToJsonWriter(jsonWriter); BackgroundBean bean = this; + // 原图配置序列化 jsonWriter.name("backgroundFileName").value(bean.getBackgroundFileName()); - jsonWriter.name("backgroundFilePath").value(bean.getBackgroundFilePath()); // 新增字段:背景原图完整路径 + jsonWriter.name("backgroundFilePath").value(bean.getBackgroundFilePath()); jsonWriter.name("backgroundFileInfo").value(bean.getBackgroundFileInfo()); + // 控制字段序列化 jsonWriter.name("isUseBackgroundFile").value(bean.isUseBackgroundFile()); + // 压缩图配置序列化 jsonWriter.name("backgroundScaledCompressFileName").value(bean.getBackgroundScaledCompressFileName()); jsonWriter.name("backgroundScaledCompressFilePath").value(bean.getBackgroundScaledCompressFilePath()); // 关键:新字段序列化(核心) jsonWriter.name("isUseBackgroundScaledCompressFile").value(bean.isUseBackgroundScaledCompressFile()); - // 兼容旧版本:保留旧字段名写入(可选,避免旧版本Bean读取时缺失字段) - jsonWriter.name("isUseScaledCompress").value(bean.isUseBackgroundScaledCompressFile()); + // 兼容旧版本:保留旧字段名写入(避免旧版本Bean读取时缺失字段) + jsonWriter.name(OLD_FIELD_USE_SCALED_COMPRESS).value(bean.isUseBackgroundScaledCompressFile()); + // 裁剪配置与像素颜色序列化 jsonWriter.name("backgroundWidth").value(bean.getBackgroundWidth()); jsonWriter.name("backgroundHeight").value(bean.getBackgroundHeight()); jsonWriter.name("pixelColor").value(bean.getPixelColor()); + LogUtils.d(TAG, "writeThisToJsonWriter: JSON 序列化完成,已兼容旧字段"); } /** @@ -172,7 +202,7 @@ public class BackgroundBean extends BaseBean implements Serializable { bean.setBackgroundFileName(jsonReader.nextString()); break; case "backgroundFilePath": - bean.setBackgroundFilePath(jsonReader.nextString()); // 新增字段:读取背景原图完整路径 + bean.setBackgroundFilePath(jsonReader.nextString()); break; case "backgroundFileInfo": bean.setBackgroundFileInfo(jsonReader.nextString()); @@ -186,13 +216,15 @@ public class BackgroundBean extends BaseBean implements Serializable { case "backgroundScaledCompressFilePath": bean.setBackgroundScaledCompressFilePath(jsonReader.nextString()); break; - // 关键:读取新字段(优先) case "isUseBackgroundScaledCompressFile": + // 关键:读取新字段(优先) bean.setIsUseBackgroundScaledCompressFile(jsonReader.nextBoolean()); + LogUtils.d(TAG, "readBeanFromJsonReader: 读取新字段 isUseBackgroundScaledCompressFile 完成"); break; - // 兼容旧版本:读取旧字段(若新字段未读取,则用旧字段值) - case "isUseScaledCompress": + case OLD_FIELD_USE_SCALED_COMPRESS: + // 兼容旧版本:读取旧字段(若新字段未读取,则用旧字段值) tempUseScaledCompress = jsonReader.nextBoolean(); + LogUtils.d(TAG, "readBeanFromJsonReader: 读取旧字段 isUseScaledCompress 完成"); break; case "backgroundWidth": bean.setBackgroundWidth(jsonReader.nextInt()); @@ -204,32 +236,36 @@ public class BackgroundBean extends BaseBean implements Serializable { bean.setPixelColor(jsonReader.nextInt()); break; default: - jsonReader.skipValue(); // 跳过未知字段,兼容旧版本Bean(避免崩溃) + jsonReader.skipValue(); + LogUtils.w(TAG, String.format("readBeanFromJsonReader: 跳过未知字段 %s", name)); break; } } jsonReader.endObject(); // 兼容逻辑:若新字段未被赋值(旧版本JSON无此字段),则用旧字段值填充 - if (!jsonReader.toString().contains("isUseBackgroundScaledCompressFile")) { + if (!bean.isUseBackgroundScaledCompressFile()) { bean.setIsUseBackgroundScaledCompressFile(tempUseScaledCompress); + LogUtils.d(TAG, "readBeanFromJsonReader: 旧字段值已填充到新字段"); } + LogUtils.d(TAG, "readBeanFromJsonReader: JSON 反序列化完成"); return bean; } - // ====================================== 辅助方法(同步更新重命名字段)====================================== + // ====================== 辅助方法(重置配置、配置校验,补充调试日志) ====================== /** * 重置背景配置(适配“取消背景”功能,同步重置重命名字段) */ public void resetBackgroundConfig() { this.backgroundFileName = ""; - this.backgroundFilePath = ""; // 新增:重置背景原图完整路径 + this.backgroundFilePath = ""; this.backgroundScaledCompressFileName = ""; this.backgroundScaledCompressFilePath = ""; this.backgroundFileInfo = ""; this.isUseBackgroundFile = false; - this.isUseBackgroundScaledCompressFile = false; // 重命名字段:重置为false - this.backgroundWidth = 100; - this.backgroundHeight = 100; + this.isUseBackgroundScaledCompressFile = false; + this.backgroundWidth = DEFAULT_DIMENSION; + this.backgroundHeight = DEFAULT_DIMENSION; + LogUtils.d(TAG, "resetBackgroundConfig: 背景配置已重置为默认值"); } /** @@ -240,16 +276,21 @@ public class BackgroundBean extends BaseBean implements Serializable { public boolean isBackgroundConfigValid() { // 启用背景图时,需确保:原图路径/文件名 或 压缩图路径/文件名 非空 if (!isUseBackgroundFile) { + LogUtils.d(TAG, "isBackgroundConfigValid: 未启用背景图,配置无效"); return false; } // 原图校验:路径非空 或 文件名非空 boolean isOriginalValid = !backgroundFilePath.isEmpty() || !backgroundFileName.isEmpty(); // 压缩图校验:启用压缩图时,路径/文件名需非空 boolean isCompressValid = true; - if (isUseBackgroundScaledCompressFile()) { // 重命名字段:判断是否启用压缩图 + if (isUseBackgroundScaledCompressFile()) { isCompressValid = !backgroundScaledCompressFilePath.isEmpty() || !backgroundScaledCompressFileName.isEmpty(); } // 逻辑:启用压缩图则需压缩图有效;不启用压缩图则需原图有效 - return isUseBackgroundScaledCompressFile() ? isCompressValid : isOriginalValid; + boolean isValid = isUseBackgroundScaledCompressFile() ? isCompressValid : isOriginalValid; + LogUtils.d(TAG, String.format("isBackgroundConfigValid: 背景配置有效性为 %b(启用压缩图:%b,原图有效:%b,压缩图有效:%b)", + isValid, isUseBackgroundScaledCompressFile(), isOriginalValid, isCompressValid)); + return isValid; } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java index 7960ac4..0353ed7 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryData.java @@ -1,26 +1,83 @@ package cc.winboll.studio.powerbell.models; +import cc.winboll.studio.libappbase.LogUtils; + /** + * 电池报告数据模型 + * 适配 API30,存储当前电量、放电时间、充电时间核心数据 + * 支持参数校验与调试日志输出 * @Author ZhanGSKen * @Date 2025/03/22 14:30:51 * @Describe 电池报告数据模型 */ public class BatteryData { - + // ====================== 静态常量(首屏可见,统一管理) ====================== public static final String TAG = "BatteryData"; + // 字段校验常量(避免硬编码,统一管理) + private static final int BATTERY_MIN = 0; + private static final int BATTERY_MAX = 100; + private static final String EMPTY_TIME = "00:00:00"; - private int currentLevel; - private String dischargeTime; - private String chargeTime; + // ====================== 成员变量(按功能分类:电量→时间) ====================== + private int currentLevel; // 当前电池电量(0-100) + private String dischargeTime; // 放电时间 + private String chargeTime; // 充电时间 - public BatteryData(int currentLevel, String dischargeTime, String chargeTime) { - this.currentLevel = currentLevel; - this.dischargeTime = dischargeTime; - this.chargeTime = chargeTime; + // ====================== 构造方法(按参数重载排序,补充校验与日志) ====================== + /** + * 无参构造器(适配 JSON 反序列化、反射实例化场景) + */ + public BatteryData() { + this.currentLevel = BATTERY_MIN; + this.dischargeTime = EMPTY_TIME; + this.chargeTime = EMPTY_TIME; + LogUtils.d(TAG, "BatteryData: 无参构造初始化完成,默认值已设置"); } - public int getCurrentLevel() { return currentLevel; } - public String getDischargeTime() { return dischargeTime; } - public String getChargeTime() { return chargeTime; } + /** + * 带参构造器(核心构造,初始化所有字段) + * @param currentLevel 当前电量(0-100) + * @param dischargeTime 放电时间 + * @param chargeTime 充电时间 + */ + public BatteryData(int currentLevel, String dischargeTime, String chargeTime) { + // 电量范围校验(0-100,异常值置为0) + this.currentLevel = currentLevel >= BATTERY_MIN && currentLevel <= BATTERY_MAX + ? currentLevel : BATTERY_MIN; + // 时间字段防 null(空值置为默认空时间) + this.dischargeTime = dischargeTime == null ? EMPTY_TIME : dischargeTime; + this.chargeTime = chargeTime == null ? EMPTY_TIME : chargeTime; + + // 调试日志:输出入参与最终赋值结果 + LogUtils.d(TAG, String.format("BatteryData: 带参构造初始化完成 | 当前电量:%d(输入:%d)| 放电时间:%s(输入:%s)| 充电时间:%s(输入:%s)", + this.currentLevel, currentLevel, + this.dischargeTime, dischargeTime, + this.chargeTime, chargeTime)); + } + + // ====================== Getter 方法(按成员变量顺序排列,补充日志可选) ====================== + /** + * 获取当前电池电量 + * @return 当前电量(0-100) + */ + public int getCurrentLevel() { + return currentLevel; + } + + /** + * 获取放电时间 + * @return 放电时间 + */ + public String getDischargeTime() { + return dischargeTime; + } + + /** + * 获取充电时间 + * @return 充电时间 + */ + public String getChargeTime() { + return chargeTime; + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java index 0d5a9ce..4a211d5 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/models/BatteryInfoBean.java @@ -3,47 +3,89 @@ package cc.winboll.studio.powerbell.models; import android.util.JsonReader; import android.util.JsonWriter; import cc.winboll.studio.libappbase.BaseBean; +import cc.winboll.studio.libappbase.LogUtils; import java.io.IOException; import java.io.Serializable; +/** + * 电池信息数据模型 + * 适配 API30,存储电量时间戳与电量值,支持 JSON 序列化/反序列化 + * 修复字段拼写错误,补充数据校验与调试日志 + * @Author ZhanGSKen + * @Describe 电池信息数据模型 + */ public class BatteryInfoBean extends BaseBean implements Serializable { - + // ====================== 静态常量(首屏可见,统一管理) ====================== public static final String TAG = "BatteryInfoBean"; + // 字段校验常量(避免硬编码,统一管理) + private static final int BATTERY_MIN = 0; + private static final int BATTERY_MAX = 100; + private static final long DEFAULT_TIMESTAMP = 0L; + private static final int DEFAULT_BATTERY_VALUE = 0; - // 记录电量的时间戳 - long timeStamp; - // 电量值 - int battetyValue; - + // ====================== 成员变量(修复拼写错误:battetyValue → batteryValue) ====================== + private long timeStamp; // 记录电量的时间戳 + private int batteryValue; // 电量值(0-100) + + // ====================== 构造方法(按参数重载排序,补充校验与日志) ====================== + /** + * 无参构造器(JSON 反序列化、反射实例化必备) + */ public BatteryInfoBean() { - this.timeStamp = 0; - this.battetyValue = 0; + this.timeStamp = DEFAULT_TIMESTAMP; + this.batteryValue = DEFAULT_BATTERY_VALUE; + LogUtils.d(TAG, "BatteryInfoBean: 无参构造初始化完成,默认时间戳:" + timeStamp + ",默认电量:" + batteryValue); } - public BatteryInfoBean(long timeStamp, int battetyValue) { + /** + * 带参构造器(核心构造,初始化所有字段) + * @param timeStamp 电量记录时间戳 + * @param batteryValue 电量值(0-100) + */ + public BatteryInfoBean(long timeStamp, int batteryValue) { this.timeStamp = timeStamp; - this.battetyValue = battetyValue; + // 电量范围校验(0-100,异常值置为默认值) + this.batteryValue = batteryValue >= BATTERY_MIN && batteryValue <= BATTERY_MAX + ? batteryValue : DEFAULT_BATTERY_VALUE; + LogUtils.d(TAG, String.format("BatteryInfoBean: 带参构造初始化完成 | 时间戳:%d | 电量:%d(输入:%d)", + this.timeStamp, this.batteryValue, batteryValue)); } + // ====================== Setter/Getter 方法(按成员变量顺序排列,修复拼写错误,补充日志) ====================== + /** + * 设置电量记录时间戳 + * @param timeStamp 时间戳 + */ public void setTimeStamp(long timeStamp) { this.timeStamp = timeStamp; + LogUtils.d(TAG, "setTimeStamp: 时间戳设置为 " + timeStamp); } public long getTimeStamp() { return timeStamp; } - public void setBattetyValue(int battetyValue) { - this.battetyValue = battetyValue; + /** + * 设置电量值(修复拼写错误:battetyValue → batteryValue) + * @param batteryValue 电量值(0-100) + */ + public void setBatteryValue(int batteryValue) { + this.batteryValue = batteryValue >= BATTERY_MIN && batteryValue <= BATTERY_MAX + ? batteryValue : DEFAULT_BATTERY_VALUE; + LogUtils.d(TAG, String.format("setBatteryValue: 电量设置为 %d(输入:%d)", + this.batteryValue, batteryValue)); } - public int getBattetyValue() { - return battetyValue; + public int getBatteryValue() { + return batteryValue; } + // ====================== JSON 序列化/反序列化方法(修复字段拼写错误,补充调试日志) ====================== @Override public String getName() { - return BatteryInfoBean.class.getName(); + String className = BatteryInfoBean.class.getName(); + LogUtils.d(TAG, "getName: 类名标识为 " + className); + return className; } @Override @@ -51,7 +93,9 @@ public class BatteryInfoBean extends BaseBean implements Serializable { super.writeThisToJsonWriter(jsonWriter); BatteryInfoBean bean = this; jsonWriter.name("timeStamp").value(bean.getTimeStamp()); - jsonWriter.name("battetyValue").value(bean.getBattetyValue()); + // 修复 JSON 字段名拼写错误:battetyValue → batteryValue + jsonWriter.name("batteryValue").value(bean.getBatteryValue()); + LogUtils.d(TAG, "writeThisToJsonWriter: JSON 序列化完成 | 时间戳:" + bean.getTimeStamp() + ",电量:" + bean.getBatteryValue()); } @Override @@ -60,16 +104,28 @@ public class BatteryInfoBean extends BaseBean implements Serializable { jsonReader.beginObject(); while (jsonReader.hasNext()) { String name = jsonReader.nextName(); - if (name.equals("timeStamp")) { - bean.setTimeStamp(jsonReader.nextLong()); - } else if (name.equals("battetyValue")) { - bean.setBattetyValue(jsonReader.nextInt()); - } else { - jsonReader.skipValue(); + switch (name) { + case "timeStamp": + bean.setTimeStamp(jsonReader.nextLong()); + break; + case "batteryValue": + bean.setBatteryValue(jsonReader.nextInt()); + break; + // 兼容旧字段名(battetyValue),避免旧配置解析失败 + case "battetyValue": + int oldBatteryValue = jsonReader.nextInt(); + bean.setBatteryValue(oldBatteryValue); + LogUtils.w(TAG, "readBeanFromJsonReader: 读取旧字段 battetyValue,已兼容为 batteryValue,值:" + oldBatteryValue); + break; + default: + jsonReader.skipValue(); + LogUtils.w(TAG, "readBeanFromJsonReader: 跳过未知字段 " + name); + break; } } - // 结束 JSON 对象 jsonReader.endObject(); + LogUtils.d(TAG, "readBeanFromJsonReader: JSON 反序列化完成 | 时间戳:" + bean.getTimeStamp() + ",电量:" + bean.getBatteryValue()); return bean; } } + diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java index 168dd38..c1a5bbb 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppCacheUtils.java @@ -64,7 +64,7 @@ public class AppCacheUtils { return; } // 对比最后一条记录的电量值,避免重复添加 - int lastBatteryValue = mBatteryInfoList.get(mBatteryInfoList.size() - 1).getBattetyValue(); + int lastBatteryValue = mBatteryInfoList.get(mBatteryInfoList.size() - 1).getBatteryValue(); if (lastBatteryValue != batteryValue) { addChangingTimeToList(batteryValue); LogUtils.d(TAG, "addChangingTime():电量变化,添加新记录(原电量:" + lastBatteryValue + ",新电量:" + batteryValue + ")"); @@ -107,7 +107,7 @@ public class AppCacheUtils { } BatteryInfoBean batteryInfo = new BatteryInfoBean(System.currentTimeMillis(), batteryValue); mBatteryInfoList.add(batteryInfo); - LogUtils.d(TAG, "addChangingTimeToList():添加新记录 - 电量:" + batteryInfo.getBattetyValue() + ",时间戳:" + batteryInfo.getTimeStamp()); + LogUtils.d(TAG, "addChangingTimeToList():添加新记录 - 电量:" + batteryInfo.getBatteryValue() + ",时间戳:" + batteryInfo.getTimeStamp()); saveAppCacheData(); } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/StringUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/StringUtils.java index 9cb966a..8940be5 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/StringUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/StringUtils.java @@ -53,7 +53,7 @@ public class StringUtils { continue; } // 获取电量和时间跨度 - int batteryValue = currentBean.getBattetyValue(); + int batteryValue = currentBean.getBatteryValue(); String timeSpan = getTimespanDifference(currentBean.getTimeStamp(), nextBean.getTimeStamp()); // 倒序拼接 result = batteryValue + "% " + timeSpan + " " + result; @@ -88,7 +88,7 @@ public class StringUtils { continue; } // 获取电量和时间跨度 - int batteryValue = currentBean.getBattetyValue(); + int batteryValue = currentBean.getBatteryValue(); String timeSpan = getTimespanDifference(currentBean.getTimeStamp(), nextBean.getTimeStamp()); // 倒序拼接(带换行) result = "\n" + batteryValue + "%\n " + timeSpan + " " + result;