From 78971006594fc2fe395ff0a287456f8b3931540d Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Fri, 21 Nov 2025 20:27:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8E=86=E5=8F=B2=E8=83=8C?= =?UTF-8?q?=E6=99=AF=E5=9B=BE=E7=89=87=E4=BF=9D=E5=AD=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../studio/powerbell/MainActivity.java | 14 +- .../dialogs/NetworkBackgroundDialog.java | 79 ++++--- .../studio/powerbell/utils/PictureUtils.java | 207 ++++++++++++++++++ 4 files changed, 269 insertions(+), 35 deletions(-) create mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PictureUtils.java diff --git a/powerbell/build.properties b/powerbell/build.properties index cddc8c02..2b4b12b0 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Fri Nov 21 10:22:55 GMT 2025 +#Fri Nov 21 12:03:39 GMT 2025 stageCount=7 libraryProject= baseVersion=15.11 publishVersion=15.11.6 -buildCount=9 +buildCount=17 baseBetaVersion=15.11.7 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java index 31994b5e..f4da79e9 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/MainActivity.java @@ -1,11 +1,13 @@ package cc.winboll.studio.powerbell; +import android.Manifest; import android.app.Activity; import android.app.Fragment; import android.app.FragmentTransaction; import android.content.Intent; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.view.KeyEvent; @@ -13,6 +15,8 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.RelativeLayout; import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import cc.winboll.studio.libaes.views.ADsBannerView; import cc.winboll.studio.libappbase.LogActivity; import cc.winboll.studio.libappbase.LogUtils; @@ -37,6 +41,8 @@ public class MainActivity extends WinBoLLActivity { public static final String TAG = "MainActivity"; + private static final int REQUEST_WRITE_STORAGE_PERMISSION = 1001; + // private static final String PRIVACY_FILE = "privacy_pfs"; // private static final String PRIVACY_VALUE = "privacy_value";//0: 拒绝,1:赞同 // @@ -81,7 +87,7 @@ public class MainActivity extends WinBoLLActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mADsBannerView = findViewById(R.id.adsbanner); - + // mContainer = findViewById(R.id.ads_container); // // // 初始化主线程Handler(关键:确保广告操作在主线程执行) @@ -107,7 +113,7 @@ public class MainActivity extends WinBoLLActivity { } showFragment(mMainViewFragment); } - + @Override protected void onDestroy() { super.onDestroy(); @@ -119,7 +125,7 @@ public class MainActivity extends WinBoLLActivity { // if (mMainHandler != null) { // mMainHandler.removeCallbacksAndMessages(null); // } - if(mADsBannerView != null) { + if (mADsBannerView != null) { mADsBannerView.releaseAdResources(); } } @@ -194,7 +200,7 @@ public class MainActivity extends WinBoLLActivity { super.onResume(); reloadBackground(); setBackgroundColor(); - if(mADsBannerView != null) { + if (mADsBannerView != null) { mADsBannerView.resumeADs(); } 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 80b70cf1..28bb94c8 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,7 +1,8 @@ package cc.winboll.studio.powerbell.dialogs; +import android.app.Activity; import android.content.Context; -import android.graphics.drawable.Drawable; +import android.content.DialogInterface; import android.os.Handler; import android.os.Message; import android.view.LayoutInflater; @@ -14,12 +15,11 @@ 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.ImageDownloader; +import cc.winboll.studio.powerbell.utils.PictureUtils; +import cc.winboll.studio.powerbell.views.BackgroundView; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import cc.winboll.studio.powerbell.views.BackgroundView; -import android.content.DialogInterface; /** * @Author ZhanGSKen&豆包大模型 @@ -163,7 +163,7 @@ public class NetworkBackgroundDialog extends AlertDialog { LogUtils.d("NetworkBackgroundDialog", "确认按钮点击"); // 确定预览背景资源 bvBackgroundPreview.saveToBackgroundSources(previewFilePath); - + dismiss(); // 关闭对话框 if (listener != null) { listener.onConfirm(); @@ -176,12 +176,13 @@ public class NetworkBackgroundDialog extends AlertDialog { @Override public void onClick(View v) { LogUtils.d("NetworkBackgroundDialog", "确认预览点击"); - String url = etURL.getText().toString().trim(); - if (url.isEmpty()) { - ToastUtils.show("请输入图片链接"); - return; - } - ImageDownloader.getInstance(mContext).downloadImage(url, mDownloadCallback); + downloadImageToAlbumAndPreview(); + /*String url = etURL.getText().toString().trim(); + if (url.isEmpty()) { + ToastUtils.show("请输入图片链接"); + return; + } + ImageDownloader.getInstance(mContext).downloadImage(url, mDownloadCallback);*/ } }); } @@ -204,7 +205,7 @@ public class NetworkBackgroundDialog extends AlertDialog { // 预览背景 previewFilePath = filePath; bvBackgroundPreview.previewBackgroundImage(previewFilePath); - + LogUtils.d(TAG, "图片预览成功:" + filePath); } catch (Exception e) { @@ -248,23 +249,43 @@ public class NetworkBackgroundDialog extends AlertDialog { this.listener = listener; } - ImageDownloader.DownloadCallback mDownloadCallback = new ImageDownloader.DownloadCallback() { - @Override - public void onSuccess(String filePath) { - ToastUtils.show("图片下载成功:" + filePath); - LogUtils.d(TAG, filePath); - // 发送消息到主线程,携带图片路径 - Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, filePath); - mUiHandler.sendMessage(successMsg); - } + /*ImageDownloader.DownloadCallback mDownloadCallback = new ImageDownloader.DownloadCallback() { + @Override + public void onSuccess(String filePath) { + ToastUtils.show("图片下载成功:" + filePath); + LogUtils.d(TAG, filePath); + // 发送消息到主线程,携带图片路径 + Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, filePath); + mUiHandler.sendMessage(successMsg); + } - @Override - public void onFailure(String errorMsg) { - ToastUtils.show("下载失败:" + errorMsg); - LogUtils.e(TAG, errorMsg); - // 发送图片加载失败消息 - mUiHandler.sendEmptyMessage(MSG_IMAGE_LOAD_FAILED); - } - }; + @Override + public void onFailure(String errorMsg) { + ToastUtils.show("下载失败:" + errorMsg); + LogUtils.e(TAG, errorMsg); + // 发送图片加载失败消息 + mUiHandler.sendEmptyMessage(MSG_IMAGE_LOAD_FAILED); + } + };*/ + + void downloadImageToAlbumAndPreview() { + //String imgUrl = "https://example.com/test.jpg"; + String imgUrl = etURL.getText().toString(); + PictureUtils.downloadImageToAlbum(mContext, imgUrl, new PictureUtils.DownloadCallback(){ + @Override + public void onSuccess(String savePath) { + ToastUtils.show("下载成功:" + savePath); + // 发送消息到主线程,携带图片路径 + Message successMsg = mUiHandler.obtainMessage(MSG_IMAGE_LOAD_SUCCESS, savePath); + mUiHandler.sendMessage(successMsg); + } + + @Override + public void onFailure(Exception e) { + ToastUtils.show("下载失败:" + e.getMessage()); + } + }); + + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PictureUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PictureUtils.java new file mode 100644 index 00000000..17e0c3df --- /dev/null +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/PictureUtils.java @@ -0,0 +1,207 @@ +package cc.winboll.studio.powerbell.utils; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Environment; +import android.util.Log; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/21 18:55 + * @Describe + * 图片下载工具类(指定目录保存:Pictures/PowerBell/BackgroundHistory) + */ +public class PictureUtils { + private static final String TAG = "PictureUtils"; + private static final String ROOT_DIR = "PowerBell/BackgroundHistory"; // 自定义目录结构 + private static OkHttpClient sOkHttpClient; + + static { + sOkHttpClient = new OkHttpClient(); + } + + /** + * 下载网络图片到指定目录(外部存储/Pictures/PowerBell/BackgroundHistory) + * @param context 上下文(用于通知相册刷新) + * @param imgUrl 图片网络URL + * @param callback 下载结果回调(成功/失败) + */ + public static void downloadImageToAlbum(final Context context, final String imgUrl, final DownloadCallback callback) { + // 检查参数合法性 + if (context == null) { + if (callback != null) { + callback.onFailure(new IllegalArgumentException("Context不能为空")); + } + return; + } + if (imgUrl == null || imgUrl.isEmpty()) { + if (callback != null) { + callback.onFailure(new IllegalArgumentException("图片URL为空")); + } + return; + } + + startDownload(context, imgUrl, callback); + } + + /** + * 执行实际的下载逻辑 + */ + private static void startDownload(final Context context, final String imgUrl, final DownloadCallback callback) { + Request request = new Request.Builder().url(imgUrl).build(); + sOkHttpClient.newCall(request).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) throws IOException { + if (!response.isSuccessful()) { + if (callback != null) { + callback.onFailure(new IOException("请求失败,响应码:" + response.code())); + } + return; + } + + InputStream inputStream = null; + FileOutputStream outputStream = null; + try { + inputStream = response.body().byteStream(); + // 1. 获取并创建指定保存目录(外部存储/Pictures/PowerBell/BackgroundHistory) + File saveDir = getTargetSaveDir(context); + if (!saveDir.exists()) { + boolean isDirCreated = saveDir.mkdirs(); // 递归创建多级目录 + if (!isDirCreated) { + if (callback != null) { + callback.onFailure(new IOException("创建目录失败:" + saveDir.getAbsolutePath())); + } + return; + } + } + + // 2. 解析图片后缀 + String fileSuffix = getImageSuffix(imgUrl, response); + // 3. 生成时间戳文件名 + String fileName = generateTimeFileName() + fileSuffix; + // 4. 创建文件 + final File saveFile = new File(saveDir, fileName); + + // 5. 写入文件 + outputStream = new FileOutputStream(saveFile); + byte[] buffer = new byte[1024 * 4]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, len); + } + outputStream.flush(); + + // 6. 通知相册刷新(使图片显示在系统相册中) + notifyAlbumRefresh(context, saveFile); + + // 成功回调 + if (callback != null) { + callback.onSuccess(saveFile.getAbsolutePath()); + } + + } catch (Exception e) { + Log.e(TAG, "下载图片异常", e); + if (callback != null) { + callback.onFailure(e); + } + } finally { + // 关闭资源 + if (inputStream != null) inputStream.close(); + if (outputStream != null) outputStream.close(); + if (response.body() != null) response.body().close(); + } + } + + @Override + public void onFailure(Call call, final IOException e) { + Log.e(TAG, "下载图片失败", e); + if (callback != null) { + callback.onFailure(e); + } + } + }); + } + + /** + * 获取目标保存目录:外部存储/Pictures/PowerBell/BackgroundHistory + */ + private static File getTargetSaveDir(Context context) { + // 优先使用公共Pictures目录(兼容多数设备) + File publicPicturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + if (publicPicturesDir.exists()) { + return new File(publicPicturesDir, ROOT_DIR); + } + // 备选:应用私有Pictures目录(若公共目录不可用) + File appPicturesDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); + if (appPicturesDir != null) { + return new File(appPicturesDir, ROOT_DIR); + } + // 极端情况:外部存储根目录 + return new File(Environment.getExternalStorageDirectory(), ROOT_DIR); + } + + /** + * 解析图片后缀名 + */ + private static String getImageSuffix(String imgUrl, Response response) { + // 优先从URL解析 + if (imgUrl.lastIndexOf('.') != -1) { + String suffix = imgUrl.substring(imgUrl.lastIndexOf('.')); + if (suffix.length() <= 5 && (suffix.contains("png") || suffix.contains("jpg") || suffix.contains("jpeg") || suffix.contains("gif"))) { + return suffix.toLowerCase(Locale.getDefault()); + } + } + // 从响应头解析 + String contentType = response.header("Content-Type"); + if (contentType != null) { + if (contentType.contains("png")) return ".png"; + if (contentType.contains("jpeg") || contentType.contains("jpg")) return ".jpg"; + if (contentType.contains("gif")) return ".gif"; + } + return ".jpg"; // 默认后缀 + } + + /** + * 生成时间戳文件名 + */ + private static String generateTimeFileName() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault()); + return sdf.format(new Date()); + } + + /** + * 通知相册刷新 + */ + private static void notifyAlbumRefresh(Context context, File file) { + try { + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + Uri uri = Uri.fromFile(file); + intent.setData(uri); + context.sendBroadcast(intent); + } catch (Exception e) { + Log.e(TAG, "通知相册刷新失败", e); + } + } + + /** + * 下载结果回调接口 + */ + public interface DownloadCallback { + void onSuccess(String savePath); // 下载成功(子线程回调) + void onFailure(Exception e); // 下载失败(子线程回调) + } +} +