Compare commits

...

18 Commits

Author SHA1 Message Date
bc8a63867c <powerbell>APK 15.11.10 release Publish. 2025-11-27 19:24:29 +08:00
2a10cb493d 更新AES类库,更新广告控制UI界面。 2025-11-27 19:22:05 +08:00
ea7e2f8366 <powerbell>APK 15.11.9 release Publish. 2025-11-27 14:46:03 +08:00
d073a86b9b 添加应用设置与米盟广告SDK设置控件。 2025-11-27 14:43:53 +08:00
e76427eac8 修复背景图片右边有缝隙,以及像素背景设置的问题。 2025-11-27 14:25:37 +08:00
af53216af3 添加中文资源字符串 2025-11-27 13:34:55 +08:00
3ae56bb202 升级基础类库 2025-11-27 13:27:49 +08:00
09f1974c8e 更新类标签 2025-11-27 08:58:41 +08:00
721c93c4b2 更新应用快捷菜单提示 2025-11-27 08:51:06 +08:00
5a9a138463 <powerbell>APK 15.11.8 release Publish. 2025-11-26 16:27:33 +08:00
ae601c1445 <powerbell>Start New Stage Version. 2025-11-26 16:24:41 +08:00
c5cd274b0f 添加多主题图标切换方案 2025-11-26 16:23:04 +08:00
07a53da918 更新应用中文名称 2025-11-25 21:29:07 +08:00
2aaf18f29f <powerbell>APK 15.11.7 release Publish. 2025-11-21 21:24:51 +08:00
9892f3de2d <powerbell>Start New Stage Version. 2025-11-21 21:23:06 +08:00
ZhanGSKen
c06a325c42 基本实现背景图层更换,操作流程与图片资源管理部分未完善。 2025-11-21 21:22:01 +08:00
ZhanGSKen
7897100659 添加历史背景图片保存功能 2025-11-21 20:27:35 +08:00
ZhanGSKen
51793077bd 添加图片设置预览功能与一些调试入口。 2025-11-21 18:24:22 +08:00
33 changed files with 1407 additions and 327 deletions

View File

@@ -77,7 +77,7 @@ dependencies {
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'
implementation 'cc.winboll.studio:libaes:15.11.6'
implementation 'cc.winboll.studio:libaes:15.11.8'
implementation 'cc.winboll.studio:libappbase:15.11.0'
//api fileTree(dir: 'libs', include: ['*.aar'])

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Fri Nov 21 14:24:01 HKT 2025
stageCount=7
#Thu Nov 27 19:24:29 HKT 2025
stageCount=11
libraryProject=
baseVersion=15.11
publishVersion=15.11.6
publishVersion=15.11.10
buildCount=0
baseBetaVersion=15.11.7
baseBetaVersion=15.11.11

View File

@@ -6,31 +6,6 @@
tools:replace="android:icon"
android:icon="@drawable/ic_launcher_beta">
<activity
android:name="cc.winboll.studio.powerbell.MainActivity"
android:launchMode="singleTask"
android:exported="true">
</activity>
<activity android:name="cc.winboll.studio.powerbell.unittest.MainUnitTestActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.powerbell.beta.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
</application>
</manifest>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PowerBell☆</string>
<string name="app_name_cn1">能源钟★</string>
<string name="app_name_cn2">泡额呗额☆</string>
</resources>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<!--<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell.beta"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -66,6 +66,82 @@
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true"
android:launchMode="singleTask">
</activity>
<activity android:name=".activities.CrashActivity"/>
<activity-alias
android:name=".MainActivityEN1"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmainen1"/>
</activity-alias>
<activity-alias
android:name=".MainActivityCN1"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name_cn1"
android:icon="@drawable/ic_launcher"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn1"/>
</activity-alias>
<activity-alias
android:name=".MainActivityCN2"
android:targetActivity=".MainActivity"
android:exported="true"
android:label="@string/app_name_cn2"
android:icon="@drawable/ic_launcher"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcutsmaincn2"/>
</activity-alias>
<activity
android:name="cc.winboll.studio.powerbell.activities.ClearRecordActivity"
android:parentActivityName="cc.winboll.studio.powerbell.MainActivity"
@@ -138,7 +214,24 @@
<activity android:name="cc.winboll.studio.powerbell.activities.BatteryReportActivity"/>
<activity android:name="cc.winboll.studio.powerbell.unittest.MainUnitTestActivity"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
<activity android:name="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"/>
<activity android:name="cc.winboll.studio.powerbell.activities.SettingsActivity"/>
</application>
</manifest>
</manifest>

View File

@@ -12,8 +12,15 @@ import java.io.File;
public class App extends GlobalApplication {
public static final String TAG = "GlobalApplication";
public static final String TAG = "App";
public static final String COMPONENT_EN1 = "cc.winboll.studio.powerbell.MainActivityEN1";
public static final String COMPONENT_CN1 = "cc.winboll.studio.powerbell.MainActivityCN1";
public static final String COMPONENT_CN2 = "cc.winboll.studio.powerbell.MainActivityCN2";
public static final String ACTION_SWITCHTO_EN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1";
public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1";
public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2";
// 数据配置存储工具
static AppConfigUtils _mAppConfigUtils;
static AppCacheUtils _mAppCacheUtils;

View File

@@ -20,9 +20,11 @@ import cc.winboll.studio.powerbell.activities.AboutActivity;
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
import cc.winboll.studio.powerbell.activities.BatteryReportActivity;
import cc.winboll.studio.powerbell.activities.ClearRecordActivity;
import cc.winboll.studio.powerbell.activities.SettingsActivity;
import cc.winboll.studio.powerbell.activities.WinBoLLActivity;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
import cc.winboll.studio.powerbell.fragments.MainViewFragment;
import cc.winboll.studio.powerbell.unittest.MainUnitTestActivity;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
/**
@@ -37,6 +39,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 +85,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关键确保广告操作在主线程执行
@@ -119,7 +123,7 @@ public class MainActivity extends WinBoLLActivity {
// if (mMainHandler != null) {
// mMainHandler.removeCallbacksAndMessages(null);
// }
if(mADsBannerView != null) {
if (mADsBannerView != null) {
mADsBannerView.releaseAdResources();
}
}
@@ -165,7 +169,7 @@ public class MainActivity extends WinBoLLActivity {
public static void reloadBackground() {
// 修复添加非空校验避免Activity已销毁时调用
if (_mMainActivity != null && !_mMainActivity.isFinishing() && !_mMainActivity.isDestroyed()) {
_mMainActivity.mMainViewFragment.loadBackground();
_mMainActivity.mMainViewFragment.reloadBackground();
}
}
@@ -194,8 +198,8 @@ public class MainActivity extends WinBoLLActivity {
super.onResume();
reloadBackground();
setBackgroundColor();
if(mADsBannerView != null) {
mADsBannerView.resumeADs();
if (mADsBannerView != null) {
mADsBannerView.resumeADs(MainActivity.this);
}
// // 修复:优化广告请求逻辑(添加生命周期判断 + 主线程执行)
@@ -221,6 +225,9 @@ public class MainActivity extends WinBoLLActivity {
public boolean onCreateOptionsMenu(Menu menu) {
mMenu = menu;
getMenuInflater().inflate(R.menu.toolbar_main, mMenu);
if (App.isDebugging()) {
getMenuInflater().inflate(R.menu.toolbar_unittest, mMenu);
}
return true;
}
@@ -228,7 +235,9 @@ public class MainActivity extends WinBoLLActivity {
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
int menuItemId = item.getItemId();
if (menuItemId == R.id.action_about) {
if (menuItemId == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class));
} else if (menuItemId == R.id.action_about) {
startActivity(new Intent(this, AboutActivity.class));
} else if (menuItemId == R.id.action_battery_report) {
startActivity(new Intent(this, BatteryReportActivity.class));
@@ -238,6 +247,8 @@ public class MainActivity extends WinBoLLActivity {
startActivity(new Intent(this, BackgroundPictureActivity.class));
} else if (menuItemId == R.id.action_log) {
LogActivity.startLogActivity(this);
} else if (menuItemId == R.id.action_unittestactivity) {
startActivity(new Intent(this, MainUnitTestActivity.class));
}
return true;
}

View File

@@ -7,16 +7,16 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog;
import cc.winboll.studio.libaes.views.AToolbar;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
@@ -24,16 +24,17 @@ import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
import cc.winboll.studio.powerbell.dialogs.BackgroundPicturePreviewDialog;
import cc.winboll.studio.powerbell.dialogs.NetworkBackgroundDialog;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
import cc.winboll.studio.powerbell.utils.FileUtils;
import cc.winboll.studio.powerbell.utils.UriUtil;
import cc.winboll.studio.powerbell.views.BackgroundView;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import cc.winboll.studio.powerbell.dialogs.NetworkBackgroundDialog;
public class BackgroundPictureActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener {
@@ -54,6 +55,10 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
private File mfTempCropPicture; // 剪裁临时文件
private File mfRecivedCropPicture; // 剪裁后的目标文件
private String preViewFileBackgroundView = "";
BackgroundView bvPreviewBackground;
boolean isCommitSettings = false;
// 静态变量
public static String _mszRecivedCropPicture = "RecivedCrop.jpg";
private static String _mszCommonFileType = "jpeg";
@@ -103,7 +108,7 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
finish(); // 点击导航栏返回按钮,触发 finish()
}
});
@@ -158,31 +163,36 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
*/
public void updatePreviewBackground() {
LogUtils.d(TAG, "updatePreviewBackground");
ImageView ivPreviewBackground = (ImageView) findViewById(R.id.activitybackgroundpictureImageView1);
//ImageView ivPreviewBackground = (ImageView) findViewById(R.id.activitybackgroundpictureImageView1);
bvPreviewBackground = (BackgroundView) findViewById(R.id.activitybackgroundpictureBackgroundView1);
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(this);
utils.loadBackgroundPictureBean();
boolean isUseBackgroundFile = utils.getBackgroundPictureBean().isUseBackgroundFile();
if (isUseBackgroundFile && mfRecivedCropPicture.exists()) {
try {
String filePath = utils.getBackgroundDir() + getBackgroundFileName();
Drawable drawable = FileUtils.getImageDrawable(filePath);
if (drawable != null) {
//drawable.setAlpha(120);
ivPreviewBackground.setImageDrawable(drawable);
}
//ToastUtils.show("背景图片已更新");
} catch (IOException e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
ToastUtils.show("背景图片加载失败");
}
//try {
String filePath = utils.getBackgroundDir() + getBackgroundFileName();
preViewFileBackgroundView = filePath;
bvPreviewBackground.previewBackgroundImage(preViewFileBackgroundView);
/*Drawable drawable = FileUtils.getImageDrawable(filePath);
if (drawable != null) {
//drawable.setAlpha(120);
//bvPreviewBackground.setImageDrawable(drawable);
}*/
//ToastUtils.show("背景图片已更新");
// } catch (IOException e) {
// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
// ToastUtils.show("背景图片加载失败");
// }
} else {
ToastUtils.show("未使用背景图片");
Drawable drawable = getResources().getDrawable(R.drawable.blank10x10);
if (drawable != null) {
drawable.setAlpha(120);
ivPreviewBackground.setImageDrawable(drawable);
}
preViewFileBackgroundView = "";
bvPreviewBackground.previewBackgroundImage(preViewFileBackgroundView);
// Drawable drawable = getResources().getDrawable(R.drawable.blank10x10);
// if (drawable != null) {
// drawable.setAlpha(120);
// bvPreviewBackground.setImageDrawable(drawable);
// }
}
}
@@ -434,13 +444,13 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
fos.close();
} catch (IOException e) {
LogUtils.e(TAG, "流关闭异常" + e);
}
}
if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
scaledBitmap.recycle();
}
}
}
}
}
if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
scaledBitmap.recycle();
}
}
}
/**
* 缩放Bitmap
@@ -619,5 +629,31 @@ public class BackgroundPictureActivity extends WinBoLLActivity implements Backgr
dialog.show();
}
/**
* 重写finish方法确保所有退出场景都触发Toast
*/
@Override
public void finish() {
if (!isCommitSettings) {
YesNoAlertDialog.show(this, "应用背景更改提示:", "是否应用预览图片?", new YesNoAlertDialog.OnDialogResultListener(){
@Override
public void onNo() {
isCommitSettings = true;
finish();
}
@Override
public void onYes() {
bvPreviewBackground.saveToBackgroundSources(preViewFileBackgroundView);
isCommitSettings = true;
finish();
}
});
} else {
super.finish();
}
}
}

View File

@@ -0,0 +1,21 @@
package cc.winboll.studio.powerbell.activities;
import android.app.Activity;
import android.os.Bundle;
import cc.winboll.studio.powerbell.R;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/27 14:26
* @Describe 应用设置窗口
*/
public class SettingsActivity extends Activity {
public static final String TAG = "SettingsActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
}
}

View File

@@ -0,0 +1,49 @@
package cc.winboll.studio.powerbell.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.utils.APPPlusUtils;
import cc.winboll.studio.powerbell.App;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/15 13:45
* @Describe 应用快捷方式活动类
*/
public class ShortcutActionActivity extends Activity {
public static final String TAG = "ShortcutActionActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 处理应用级别的切换请求
handleSwitchRequest();
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 && "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);
}
}
}

View File

@@ -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&豆包大模型<zhangsken@qq.com>
@@ -45,6 +45,7 @@ public class NetworkBackgroundDialog extends AlertDialog {
Context mContext;
// 主线程 Handler用于接收子线程消息并更新 UI
private Handler mUiHandler;
String previewFilePath;
// 按钮点击回调接口Java7 接口实现)
public interface OnDialogClickListener {
@@ -160,6 +161,9 @@ public class NetworkBackgroundDialog extends AlertDialog {
@Override
public void onClick(View v) {
LogUtils.d("NetworkBackgroundDialog", "确认按钮点击");
// 确定预览背景资源
bvBackgroundPreview.saveToBackgroundSources(previewFilePath);
dismiss(); // 关闭对话框
if (listener != null) {
listener.onConfirm();
@@ -172,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);*/
}
});
}
@@ -192,18 +197,15 @@ public class NetworkBackgroundDialog extends AlertDialog {
File imageFile = new File(filePath);
if (!imageFile.exists()) {
LogUtils.e(TAG, "图片文件不存在:" + filePath);
bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher);
ToastUtils.show("Test");
//bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher);
return;
}
fis = new FileInputStream(imageFile);
Drawable drawable = Drawable.createFromStream(fis, null);
// 设置背景(主线程安全操作)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
bvBackgroundPreview.setBackground(drawable);
} else {
bvBackgroundPreview.setBackgroundDrawable(drawable);
}
// 预览背景
previewFilePath = filePath;
bvBackgroundPreview.previewBackgroundImage(previewFilePath);
LogUtils.d(TAG, "图片预览成功:" + filePath);
} catch (Exception e) {
@@ -247,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());
}
});
}
}

View File

@@ -9,7 +9,6 @@ import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
@@ -20,15 +19,15 @@ import android.widget.TextView;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.activities.BackgroundPictureActivity;
import cc.winboll.studio.powerbell.activities.PixelPickerActivity;
import cc.winboll.studio.powerbell.beans.BackgroundPictureBean;
import cc.winboll.studio.powerbell.services.ControlCenterService;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.powerbell.utils.BackgroundPictureUtils;
import cc.winboll.studio.powerbell.utils.ServiceUtils;
import cc.winboll.studio.powerbell.views.BackgroundView;
import cc.winboll.studio.powerbell.views.BatteryDrawable;
import cc.winboll.studio.powerbell.views.VerticalSeekBar;
import java.io.File;
public class MainViewFragment extends Fragment {
@@ -72,6 +71,7 @@ public class MainViewFragment extends Fragment {
TextView mtvUsegeReminderValue;
CheckBox mcbUsegeReminderValue;
TextView mtvCurrentValue;
BackgroundView bvPreviewBackground;
@Override
@@ -79,27 +79,33 @@ public class MainViewFragment extends Fragment {
mView = inflater.inflate(R.layout.fragment_mainview, container, false);
_mMainViewFragment = MainViewFragment.this;
mAppConfigUtils = App.getAppConfigUtils(getActivity());
// 获取指定ID的View实例
final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1);
bvPreviewBackground = mView.findViewById(R.id.fragmentmainviewBackgroundView1);
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity());
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
int nPixelColor = bean.getPixelColor();
bvPreviewBackground.setBackgroundColor(nPixelColor);
/*final View mainImageView = mView.findViewById(R.id.fragmentmainviewImageView1);
// 注册OnGlobalLayoutListener
mainImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// 获取宽度和高度
int width = mainImageView.getMeasuredWidth();
int height = mainImageView.getMeasuredHeight();
// 注册OnGlobalLayoutListener
mainImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// 获取宽度和高度
int width = mainImageView.getMeasuredWidth();
int height = mainImageView.getMeasuredHeight();
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity());
BackgroundPictureBean bean = utils.loadBackgroundPictureBean();
bean.setBackgroundWidth(width);
bean.setBackgroundHeight(height);
utils.saveData();
// 移除监听器以避免内存泄漏
mainImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity());
BackgroundPictureBean bean = utils.loadBackgroundPictureBean();
bean.setBackgroundWidth(width);
bean.setBackgroundHeight(height);
utils.saveData();
// 移除监听器以避免内存泄漏
mainImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});*/
mDrawableFrame = getActivity().getDrawable(R.drawable.bg_frame);
mllLeftSeekBar = (LinearLayout) mView.findViewById(R.id.fragmentmainviewLinearLayout1);
@@ -143,6 +149,17 @@ public class MainViewFragment extends Fragment {
return mView;
}
@Override
public void onResume() {
super.onResume();
BackgroundPictureUtils utils = BackgroundPictureUtils.getInstance(getActivity());
BackgroundPictureBean bean = utils.getBackgroundPictureBean();
int nPixelColor = bean.getPixelColor();
bvPreviewBackground.setBackgroundColor(nPixelColor);
}
void setViewData() {
int nChargeReminderValue = mAppConfigUtils.getChargeReminderValue();
int nUsegeReminderValue = mAppConfigUtils.getUsegeReminderValue();
@@ -302,22 +319,23 @@ public class MainViewFragment extends Fragment {
}
}
public void loadBackground() {
BackgroundPictureBean bean = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundPictureBean();
ImageView imageView = mView.findViewById(R.id.fragmentmainviewImageView1);
String szBackgroundFilePath = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundDir() + BackgroundPictureActivity.getBackgroundFileName();
File fBackgroundFilePath = new File(szBackgroundFilePath);
LogUtils.d(TAG, "szBackgroundFilePath : " + szBackgroundFilePath);
LogUtils.d(TAG, String.format("fBackgroundFilePath.exists() %s", fBackgroundFilePath.exists()));
if (bean.isUseBackgroundFile() && fBackgroundFilePath.exists()) {
Drawable drawableBackground = Drawable.createFromPath(szBackgroundFilePath);
//drawableBackground.setAlpha(120);
imageView.setImageDrawable(drawableBackground);
} else {
Drawable drawableBackground = getActivity().getDrawable(R.drawable.blank10x10);
//drawableBackground.setAlpha(120);
imageView.setImageDrawable(drawableBackground);
}
public void reloadBackground() {
bvPreviewBackground.reloadBackgroundImage();
// BackgroundPictureBean bean = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundPictureBean();
// ImageView imageView = mView.findViewById(R.id.fragmentmainviewImageView1);
// String szBackgroundFilePath = BackgroundPictureUtils.getInstance(getActivity()).getBackgroundDir() + BackgroundPictureActivity.getBackgroundFileName();
// File fBackgroundFilePath = new File(szBackgroundFilePath);
// LogUtils.d(TAG, "szBackgroundFilePath : " + szBackgroundFilePath);
// LogUtils.d(TAG, String.format("fBackgroundFilePath.exists() %s", fBackgroundFilePath.exists()));
// if (bean.isUseBackgroundFile() && fBackgroundFilePath.exists()) {
// Drawable drawableBackground = Drawable.createFromPath(szBackgroundFilePath);
// //drawableBackground.setAlpha(120);
// imageView.setImageDrawable(drawableBackground);
// } else {
// Drawable drawableBackground = getActivity().getDrawable(R.drawable.blank10x10);
// //drawableBackground.setAlpha(120);
// imageView.setImageDrawable(drawableBackground);
// }
}
Handler mHandler = new Handler(){

View File

@@ -8,6 +8,9 @@ import androidx.fragment.app.Fragment;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.powerbell.R;
import android.widget.Button;
import cc.winboll.studio.powerbell.MainActivity;
import android.content.Intent;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
@@ -30,6 +33,14 @@ public class BackgroundViewTestFragment extends Fragment {
}
mainView = inflater.inflate(R.layout.fragment_test_backgroundview, container, false);
((Button)mainView.findViewById(R.id.btn_main_activity)).setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
getActivity().startActivity(new Intent(getActivity(), MainActivity.class));
}
});
ToastUtils.show(String.format("%s onCreate", TAG));
return mainView;

View File

@@ -0,0 +1,164 @@
package cc.winboll.studio.powerbell.utils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/26 15:54
* @Describe 应用图标切换工具类(启用组件时创建对应快捷方式)
*/
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.widget.Toast;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R;
public class APPPlusUtils {
public static final String TAG = "APPPlusUtils";
// 快捷方式配置(名称+图标,需与实际资源匹配)
// private static final String PLUS_SHORTCUT_NAME = "位置服务-Laojun";
// private static final int PLUS_SHORTCUT_ICON = R.mipmap.ic_launcher; // Laojun 图标资源
/**
* 添加Plus组件与图标
*/
public static boolean switchAppLauncherToComponent(Context context, String componentName) {
if (context == null) {
LogUtils.d(TAG, "切换失败:上下文为空");
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败", Toast.LENGTH_SHORT).show();
return false;
}
PackageManager pm = context.getPackageManager();
ComponentName plusComponentSwitchTo = new ComponentName(context, componentName);
ComponentName plusComponentEN1 = new ComponentName(context, App.COMPONENT_EN1);
ComponentName plusComponentCN1 = new ComponentName(context, App.COMPONENT_CN1);
ComponentName plusComponentCN2 = new ComponentName(context, App.COMPONENT_CN2);
try {
disableComponent(pm, plusComponentEN1);
disableComponent(pm, plusComponentCN1);
disableComponent(pm, plusComponentCN2);
enableComponent(pm, plusComponentSwitchTo);
return true;
} catch (Exception e) {
LogUtils.e(TAG, "图标切换失败:" + e.getMessage());
Toast.makeText(context, context.getString(R.string.app_name) + "图标切换失败" + e.getMessage(), Toast.LENGTH_SHORT).show();
return false;
}
}
/**
* 创建指定组件的桌面快捷方式(自动去重,兼容 Android 8.0+
* @param component 目标组件(如 LAOJUN_ACTIVITY
* @param name 快捷方式名称
* @param iconRes 快捷方式图标资源ID
* @return 是否创建成功
*/
private static boolean createComponentShortcut(Context context, ComponentName component, String name, int iconRes) {
if (context == null || component == null || name == null || iconRes == 0) {
LogUtils.d(TAG, "快捷方式创建失败:参数为空");
return false;
}
// Android 8.0+API 26+):使用 ShortcutManager系统推荐
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
PackageManager pm = context.getPackageManager();
android.content.pm.ShortcutManager shortcutManager = context.getSystemService(android.content.pm.ShortcutManager.class);
if (shortcutManager == null || !shortcutManager.isRequestPinShortcutSupported()) {
LogUtils.d(TAG, "系统不支持创建快捷方式");
return false;
}
// 检查是否已存在该组件的快捷方式(去重)
for (android.content.pm.ShortcutInfo info : shortcutManager.getPinnedShortcuts()) {
if (component.getClassName().equals(info.getIntent().getComponent().getClassName())) {
LogUtils.d(TAG, "快捷方式已存在:" + component.getClassName());
return true;
}
}
// 构建启动目标组件的意图
Intent launchIntent = new Intent(Intent.ACTION_MAIN)
.setComponent(component)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 构建快捷方式信息
android.content.pm.ShortcutInfo shortcutInfo = new android.content.pm.ShortcutInfo.Builder(context, component.getClassName())
.setShortLabel(name)
.setLongLabel(name)
.setIcon(android.graphics.drawable.Icon.createWithResource(context, iconRes))
.setIntent(launchIntent)
.build();
// 请求创建快捷方式(需用户确认)
shortcutManager.requestPinShortcut(shortcutInfo, null);
return true;
} catch (Exception e) {
LogUtils.d(TAG, "Android O+ 快捷方式创建失败:" + e.getMessage());
return false;
}
} else {
// Android 8.0 以下:使用广播(兼容旧机型)
try {
// 构建启动目标组件的意图
Intent launchIntent = new Intent(Intent.ACTION_MAIN)
.setComponent(component)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 构建创建快捷方式的广播意图
Intent installIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
installIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
installIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
installIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(context, iconRes));
installIntent.putExtra("duplicate", false); // 禁止重复创建
context.sendBroadcast(installIntent);
return true;
} catch (Exception e) {
LogUtils.d(TAG, "Android O- 快捷方式创建失败:" + e.getMessage());
return false;
}
}
}
/**
* 启用组件(带状态检查,避免重复操作)
*/
private static void enableComponent(PackageManager pm, ComponentName component) {
if (pm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
}
}
/**
* 禁用组件(带状态检查,避免重复操作)
*/
private static void disableComponent(PackageManager pm, ComponentName component) {
if (pm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS
);
}
}
}

View File

@@ -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&豆包大模型<zhangsken@qq.com>
* @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); // 下载失败(子线程回调)
}
}

View File

@@ -4,9 +4,10 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.R;
@@ -17,20 +18,23 @@ import java.io.FileOutputStream;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/11/19 18:01
* @Describe 背景图片视图控件(加载外置存储 Background/current.jpg
* @Describe 背景图片视图控件(全透明背景 + 不拉伸居中平铺 + 完全填充父视图
*/
public class BackgroundView extends RelativeLayout {
public static final String TAG = "BackgroundView";
Context mContext;
View mMianView;
// 外置存储背景图片路径:/storage/emulated/0/Android/data/应用包名/files/Background/current.jpg
private ImageView ivBackground;
private static String BACKGROUND_IMAGE_FOLDER = "Background";
private static String BACKGROUND_IMAGE_FILENAME = "current.data";
private static String backgroundSourceFilePath;
private static String BACKGROUND_IMAGE_PREVIEW_FILENAME = "current_preview.data";
private static String backgroundSourceFilePath;
private float imageAspectRatio = 1.0f; // 图片原始宽高比(控制不拉伸)
// 标记当前是否处于预览模式
private boolean isPreviewMode = false;
public BackgroundView(Context context) {
super(context);
this.mContext = context;
@@ -56,172 +60,313 @@ public class BackgroundView extends RelativeLayout {
}
void initView() {
// 1. 初始化外置存储背景图片路径(应用私有外置存储,无需动态权限)
// 1. 控件本身:完全填充父视图 + 全透明背景 + 无内边距
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
setPadding(0, 0, 0, 0); // 取消自身内边距
setBackgroundColor(0x00000000); // 全透明背景(#00000000
setBackground(new ColorDrawable(0x00000000)); // 双重保障同时设置Background为透明兼容低版本
initBackgroundImageView(); // 初始化内部ImageView全透明 + 不拉伸居中平铺)
initBackgroundImagePath();
// 2. 加载并设置背景图片
loadAndSetBackground();
loadAndSetImageViewBackground();
}
private void initBackgroundImageView() {
ivBackground = new ImageView(mContext);
// 2. ImageView初始宽高WRAP_CONTENT + 居中 + 无内边距 + 全透明背景
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); // 居中显示
layoutParams.setMargins(0, 0, 0, 0); // 取消边距
ivBackground.setLayoutParams(layoutParams);
// 3. 缩放模式FIT_CENTER不拉伸按比例显示
ivBackground.setScaleType(ImageView.ScaleType.FIT_CENTER);
ivBackground.setPadding(0, 0, 0, 0); // 取消内部padding
ivBackground.setBackgroundColor(0x00000000); // ImageView背景全透明
ivBackground.setBackground(new ColorDrawable(0x00000000)); // 双重保障(兼容低版本)
this.addView(ivBackground);
}
/**
* 初始化外置存储背景图片路径
* 路径:/storage/emulated/0/Android/data/应用包名/files/Background/current.jpg
*/
private void initBackgroundImagePath() {
// 获取应用私有外置存储的 files 目录API 19+ 无需权限)
File externalFilesDir = mContext.getExternalFilesDir(null);
if (externalFilesDir == null) {
LogUtils.e(TAG, "外置存储不可用,无法初始化背景图片路径");
return;
}
// 拼接 Background 目录和 current.jpg 文件路径
File backgroundDir = new File(externalFilesDir, "Background");
if(!backgroundDir.exists()){
backgroundDir.mkdirs();
}
//BACKGROUND_IMAGE_PATH = new File(backgroundDir, "current.jpg").getAbsolutePath();
//BACKGROUND_IMAGE_PATH = new File(backgroundDir, "current.data").getAbsolutePath();
backgroundSourceFilePath = new File(backgroundDir, BACKGROUND_IMAGE_FILENAME).getAbsolutePath();
//LogUtils.d(TAG, "背景图片路径:" + BACKGROUND_IMAGE_PATH);
File backgroundDir = new File(externalFilesDir, BACKGROUND_IMAGE_FOLDER);
if (!backgroundDir.exists()) {
backgroundDir.mkdirs();
}
backgroundSourceFilePath = new File(backgroundDir, BACKGROUND_IMAGE_FILENAME).getAbsolutePath();
}
/**
* 拷贝图片文件到背景资源目录
* @param srcBackgroundPath 源文件路径(待拷贝的图片路径)
*/
public void saveToBackgroundSources(String srcBackgroundPath) {
// 1. 初始化目标路径(确保路径有效)
initBackgroundImagePath();
if (backgroundSourceFilePath == null) {
LogUtils.e(TAG, "目标路径初始化失败,无法保存背景图片");
return;
}
// 2. 校验源文件
File srcFile = new File(srcBackgroundPath);
if (!srcFile.exists() || !srcFile.isFile()) {
LogUtils.e(TAG, String.format("源文件不存在或不是文件:%s", srcBackgroundPath));
return;
}
// 3. 创建目标目录(若不存在)
File destFile = new File(backgroundSourceFilePath);
File destDir = destFile.getParentFile();
if (destDir != null && !destDir.exists()) {
boolean isDirCreated = destDir.mkdirs();
if (!isDirCreated) {
LogUtils.e(TAG, "目标目录创建失败:" + destDir.getAbsolutePath());
return;
}
}
// 4. 文件流拷贝Java 7 原生实现,兼容低版本)
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile); // 覆盖已有文件(如需询问用户可在此处添加逻辑)
// 缓冲区拷贝(提升效率,避免频繁 IO 操作)
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush(); // 强制刷新缓冲区,确保数据完整写入
LogUtils.d(TAG, String.format("文件拷贝成功:%s -> %s", srcBackgroundPath, backgroundSourceFilePath));
} catch (Exception e) {
// 捕获所有异常,避免崩溃
LogUtils.e(TAG, String.format("文件拷贝失败:%s", e.getMessage()), e);
// 拷贝失败时删除目标文件(避免生成损坏文件)
if (destFile.exists()) {
destFile.delete();
LogUtils.d(TAG, "已删除损坏的目标文件");
}
} finally {
// 5. 关闭流Java 7 手动关闭,避免资源泄漏)
if (fis != null) {
try {
fis.close();
} catch (Exception e) {
LogUtils.e(TAG, "输入流关闭失败:" + e.getMessage());
}
}
if (fos != null) {
try {
fos.close();
} catch (Exception e) {
LogUtils.e(TAG, "输出流关闭失败:" + e.getMessage());
}
}
}
}
/**
* 加载外置存储的 current.jpg 并设置为控件背景
* 支持:文件压缩、版本兼容、异常兜底
* 拷贝图片文件到背景资源目录(正式背景)
*/
private void loadAndSetBackground() {
// 校验路径是否有效
public void saveToBackgroundSources(String srcBackgroundPath) {
initBackgroundImagePath();
if (backgroundSourceFilePath == null) {
setDefaultBackground();
LogUtils.e(TAG, "目标路径初始化失败,无法保存背景图片");
return;
}
File srcFile = new File(srcBackgroundPath);
if (!srcFile.exists() || !srcFile.isFile()) {
LogUtils.e(TAG, String.format("源文件不存在或不是文件:%s", srcBackgroundPath));
return;
}
File destFile = new File(backgroundSourceFilePath);
File destDir = destFile.getParentFile();
if (destDir != null && !destDir.exists()) {
boolean isDirCreated = destDir.mkdirs();
if (!isDirCreated) {
LogUtils.e(TAG, "目标目录创建失败:" + destDir.getAbsolutePath());
return;
}
}
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
LogUtils.d(TAG, String.format("文件拷贝成功:%s -> %s", srcBackgroundPath, backgroundSourceFilePath));
// 拷贝成功后,若处于预览模式则退出预览,加载正式背景
if (isPreviewMode) {
exitPreviewMode();
} else {
loadAndSetImageViewBackground();
}
} catch (Exception e) {
LogUtils.e(TAG, String.format("文件拷贝失败:%s", e.getMessage()), e);
if (destFile.exists()) {
destFile.delete();
LogUtils.d(TAG, "已删除损坏的目标文件");
}
} finally {
if (fis != null) {
try {
fis.close();
} catch (Exception e) {
LogUtils.e(TAG, "输入流关闭失败:" + e.getMessage());
}
}
if (fos != null) {
try {
fos.close();
} catch (Exception e) {
LogUtils.e(TAG, "输出流关闭失败:" + e.getMessage());
}
}
}
}
/**
* 预览临时图片(全透明背景 + 不拉伸居中平铺)
* @param previewImagePath 临时预览图片的路径
*/
public void previewBackgroundImage(String previewImagePath) {
if (previewImagePath == null || previewImagePath.isEmpty()) {
LogUtils.e(TAG, "预览图片路径为空");
setDefaultImageViewBackground();
return;
}
File previewFile = new File(previewImagePath);
if (!previewFile.exists() || !previewFile.isFile()) {
LogUtils.e(TAG, "预览图片不存在或不是文件:" + previewImagePath);
setDefaultImageViewBackground();
return;
}
// 计算图片原始宽高比
if (!calculateImageAspectRatio(previewFile)) {
LogUtils.e(TAG, "预览图片尺寸无效,无法预览");
setDefaultImageViewBackground();
return;
}
// 压缩加载预览图片(保持比例)
Bitmap previewBitmap = decodeBitmapWithCompress(previewFile, 1080, 1920);
if (previewBitmap == null) {
LogUtils.e(TAG, "预览图片加载失败");
setDefaultImageViewBackground();
return;
}
// 设置预览图片保持ImageView透明背景
Drawable previewDrawable = new BitmapDrawable(mContext.getResources(), previewBitmap);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
ivBackground.setBackground(previewDrawable);
} else {
ivBackground.setBackgroundDrawable(previewDrawable);
}
// 调整ImageView尺寸居中平铺不拉伸
adjustImageViewSize();
isPreviewMode = true;
LogUtils.d(TAG, "进入预览模式,预览图片路径:" + previewImagePath + ",宽高比:" + imageAspectRatio);
}
/**
* 退出预览模式,恢复显示正式背景图片
*/
public void exitPreviewMode() {
if (isPreviewMode) {
loadAndSetImageViewBackground();
isPreviewMode = false;
LogUtils.d(TAG, "退出预览模式,恢复正式背景");
}
}
/**
* 公共函数:供外部类调用,重新加载正式背景图片(刷新显示)
*/
public void reloadBackgroundImage() {
LogUtils.d(TAG, "外部调用重新加载背景图片");
initBackgroundImagePath();
loadAndSetImageViewBackground();
// 若处于预览模式,退出预览
if (isPreviewMode) {
isPreviewMode = false;
}
}
/**
* 加载正式背景图片并设置到 ImageView全透明背景 + 不拉伸居中平铺)
*/
private void loadAndSetImageViewBackground() {
if (backgroundSourceFilePath == null) {
setDefaultImageViewBackground();
return;
}
File backgroundFile = new File(backgroundSourceFilePath);
// 校验文件是否存在
if (!backgroundFile.exists() || !backgroundFile.isFile()) {
LogUtils.e(TAG, "背景图片不存在:" + backgroundSourceFilePath);
setDefaultBackground();
setDefaultImageViewBackground();
return;
}
// 3. 压缩加载 Bitmap避免 OOM
Bitmap bitmap = decodeBitmapWithCompress(backgroundFile, 1080, 1920); // 最大宽高限制
// 计算图片原始宽高比
if (!calculateImageAspectRatio(backgroundFile)) {
setDefaultImageViewBackground();
return;
}
// 压缩加载 Bitmap保持比例
Bitmap bitmap = decodeBitmapWithCompress(backgroundFile, 1080, 1920);
if (bitmap == null) {
LogUtils.e(TAG, "图片加载失败,无法解析为 Bitmap");
setDefaultBackground();
setDefaultImageViewBackground();
return;
}
// 4. 设置为控件背景(兼容 Android 低版本
// 设置图片保持ImageView透明背景
Drawable backgroundDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
this.setBackground(backgroundDrawable);
ivBackground.setBackground(backgroundDrawable);
} else {
this.setBackgroundDrawable(backgroundDrawable);
ivBackground.setBackgroundDrawable(backgroundDrawable);
}
LogUtils.d(TAG, "背景图片加载成功");
// 调整ImageView尺寸居中平铺不拉伸
adjustImageViewSize();
LogUtils.d(TAG, "ImageView 背景加载成功(全透明+不拉伸),宽高比:" + imageAspectRatio);
}
/**
* 带压缩的 Bitmap 解码(避免大图导致 OOM
* @param file 图片文件
* @param maxWidth 最大宽度限制
* @param maxHeight 最大高度限制
* @return 压缩后的 Bitmapnull 表示失败)
* 计算图片原始宽高比(宽/高
*/
private boolean calculateImageAspectRatio(File file) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageWidth = options.outWidth;
int imageHeight = options.outHeight;
if (imageWidth <= 0 || imageHeight <= 0) {
LogUtils.e(TAG, "图片尺寸无效:宽=" + imageWidth + ", 高=" + imageHeight);
return false;
}
imageAspectRatio = (float) imageWidth / imageHeight;
return true;
} catch (Exception e) {
LogUtils.e(TAG, "计算图片宽高比失败:" + e.getMessage());
return false;
}
}
/**
* 调整ImageView尺寸不拉伸居中平铺
*/
private void adjustImageViewSize() {
int parentWidth = getWidth(); // 控件宽度(已填充父视图)
int parentHeight = getHeight(); // 控件高度(已填充父视图)
if (parentWidth == 0 || parentHeight == 0) {
post(new Runnable() {
@Override
public void run() {
adjustImageViewSize();
}
});
return;
}
int imageViewWidth, imageViewHeight;
if (imageAspectRatio >= 1.0f) { // 横图
imageViewWidth = Math.min(parentWidth, (int) (parentHeight * imageAspectRatio));
imageViewHeight = (int) (imageViewWidth / imageAspectRatio);
} else { // 竖图
imageViewHeight = Math.min(parentHeight, (int) (parentWidth / imageAspectRatio));
imageViewWidth = (int) (imageViewHeight * imageAspectRatio);
}
// 应用尺寸
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) ivBackground.getLayoutParams();
layoutParams.width = imageViewWidth;
layoutParams.height = imageViewHeight;
ivBackground.setLayoutParams(layoutParams);
LogUtils.d(TAG, "ImageView 尺寸调整完成:宽=" + imageViewWidth + ", 高=" + imageViewHeight);
}
/**
* 带压缩的 Bitmap 解码(保持比例,避免 OOM
*/
private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 先获取图片尺寸,不加载到内存
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
// 计算缩放比例
int scaleX = options.outWidth / maxWidth;
int scaleY = options.outHeight / maxHeight;
int inSampleSize = Math.max(scaleX, scaleY);
if (inSampleSize <= 0) {
inSampleSize = 1; // 最小缩放比例为 1
inSampleSize = 1;
}
// 正式加载图片并压缩
options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565; // 降低像素格式,减少内存占用
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
} catch (Exception e) {
LogUtils.e(TAG, "图片压缩加载失败:" + e.getMessage());
@@ -230,11 +375,27 @@ public class BackgroundView extends RelativeLayout {
}
/**
* 设置默认背景(图片加载失败时兜底
* 设置默认背景(全透明兜底,避免空白
*/
private void setDefaultBackground() {
// 可替换为项目自定义的默认背景图
this.setBackgroundResource(R.drawable.default_background);
LogUtils.d(TAG, "已设置默认背景");
private void setDefaultImageViewBackground() {
// 关键:默认背景设为全透明(而非默认图,避免遮挡下层视图)
ivBackground.setBackground(new ColorDrawable(0x00000000)); // 全透明背景
imageAspectRatio = 1.0f;
adjustImageViewSize();
LogUtils.d(TAG, "已设置默认透明背景");
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
adjustImageViewSize();
}
/**
* 对外提供:判断当前是否处于预览模式
*/
public boolean isPreviewMode() {
return isPreviewMode;
}
}

View File

@@ -21,13 +21,15 @@
android:layout_height="match_parent"
android:id="@+id/activitybackgroundpictureRelativeLayout1"/>
<ImageView
<cc.winboll.studio.powerbell.views.BackgroundView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/activitybackgroundpictureImageView1"
android:layout_below="@id/toolbar">
android:background="#FF7381FF"
android:id="@+id/activitybackgroundpictureBackgroundView1">
</ImageView>
</cc.winboll.studio.powerbell.views.BackgroundView>
<LinearLayout
android:orientation="vertical"

View File

@@ -21,8 +21,7 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/activitymainRelativeLayout1"
android:background="#FFB7B7B7"/>
android:id="@+id/activitymainRelativeLayout1"/>
<FrameLayout
android:layout_width="match_parent"

View File

@@ -8,9 +8,8 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/activitymainunittestFrameLayout1"
android:layout_weight="1.0"/>
android:layout_height="match_parent"
android:id="@+id/activitymainunittestFrameLayout1"/>
</LinearLayout>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cc.winboll.studio.libaes.views.AToolbar
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height"
android:id="@+id/toolbar"
style="@style/DefaultAToolbar"/>
<cc.winboll.studio.libaes.views.ADsControlView
android:id="@+id/ads_control_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -5,12 +5,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
<cc.winboll.studio.powerbell.views.BackgroundView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragmentmainviewImageView1">
</ImageView>
android:background="#FF7381FF"
android:id="@+id/fragmentmainviewBackgroundView1"/>
<LinearLayout
android:orientation="vertical"

View File

@@ -7,10 +7,17 @@
android:layout_height="match_parent"
android:background="#FF7381FF">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="IT"/>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Main"
android:id="@+id/btn_main_activity"/>
</HorizontalScrollView>
</cc.winboll.studio.powerbell.views.BackgroundView>

View File

@@ -10,9 +10,9 @@
android:id="@+id/action_changepicture"
android:title="@string/item_changepicture"/>
<item
android:id="@+id/action_log"
android:title="@string/item_logview"/>
android:id="@+id/action_settings"
android:title="@string/item_settings"/>
<item
android:id="@+id/action_about"
android:title="@string/item_aboutview"/>
android:title="@string/item_about"/>
</menu>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_log"
android:title="@string/item_logview"/>
<item
android:id="@+id/action_unittestactivity"
android:title="@string/item_mainunittestactivity"/>
</menu>

View File

@@ -1,15 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PowerBell</string>
<string name="app_name_cn1">能源钟</string>
<string name="app_name_cn2">泡额呗额</string>
<string name="app_description">一个接收手机电量信息的应用,当电量值达到设定范围时会提醒用户。</string>
<string name="about_crashed">本应用崩溃了,作者水平有限,敬请谅解!</string>
<string name="item_mainview">Main View</string>
<string name="item_aboutview">About</string>
<string name="item_clearrecord">Clear Record</string>
<string name="item_changepicture">Change Picture</string>
<string name="item_devoloperoptionsview">Developer View</string>
<string name="item_logview">Log View</string>
<string name="item_sourceview">Source View</string>
<string name="item_main">主窗口</string>
<string name="item_about">关于</string>
<string name="item_settings">应用设置</string>
<string name="item_battery_report">应用耗电记录</string>
<string name="item_mainunittestactivity">开发调试窗口</string>
<string name="item_clearrecord">清理电量记录</string>
<string name="item_changepicture">更换背景图片</string>
<string name="item_devoloperoptionsview">开发调试窗口</string>
<string name="item_logview">日志窗口</string>
<string name="item_sourceview">源码窗口</string>
<string name="txt_aboveswitch">消息总开关</string>
<string name="txt_aboveswitchtips">当电量低于左边(放电状态)或高于右边(充电状态),就会发送一个提醒铃声。</string>
<string name="texthint_CustomSlideToCleanRecord">Slide Right To Clean Up APP Record.</string>

View File

@@ -1,16 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PowerBell</string>
<string name="app_name_cn1">能源钟</string>
<string name="app_name_cn2">泡额呗额</string>
<string name="app_projectname">PowerBell</string>
<string name="app_description">A mobile app that receives battery level information from a phone and alerts the user when the battery level reaches a predefined range.</string>
<string name="about_crashed">This application has crashed, the author level is limited, please understand!</string>
<string name="switchto_en1">PowerBell</string>
<string name="switchto_cn1">能源钟</string>
<string name="switchto_cn2">泡额呗额</string>
<string name="en1_switch_disabled">PowerBell X</string>
<string name="cn1_switch_disabled">能源钟 X</string>
<string name="cn2_switch_disabled">泡额呗额 X</string>
<string name="item_mainview">Main View</string>
<string name="item_aboutview">About</string>
<string name="item_about">About</string>
<string name="item_settings">Settings</string>
<string name="item_battery_report">Battery Report</string>
<string name="item_clearrecord">Clear Record</string>
<string name="item_changepicture">Change Picture</string>
<string name="item_devoloperoptionsview">Developer View</string>
<string name="item_logview">Log View</string>
<string name="item_mainunittestactivity">Debug Activity</string>
<string name="item_cleanlog">Clean Log</string>
<string name="item_sourceview">Source View</string>
<string name="txt_aboveswitch">Message master switch</string>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<!--<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
</shortcuts>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 切换启动入口的快捷菜单 -->
<!--<shortcut
android:shortcutId="switchto_en1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_en1"
android:shortcutLongLabel="@string/switchto_en1"
android:shortcutDisabledMessage="@string/en1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_EN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_en1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>-->
<shortcut
android:shortcutId="switchto_cn1"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn1"
android:shortcutLongLabel="@string/switchto_cn1"
android:shortcutDisabledMessage="@string/cn1_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn1" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="switchto_cn2"
android:enabled="true"
android:icon="@drawable/ic_launcher"
android:shortcutShortLabel="@string/switchto_cn2"
android:shortcutLongLabel="@string/switchto_cn2"
android:shortcutDisabledMessage="@string/cn2_switch_disabled">
<intent
android:action="cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"
android:targetPackage="cc.winboll.studio.powerbell"
android:targetClass="cc.winboll.studio.powerbell.activities.ShortcutActionActivity"
android:data="switchto_cn2" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

View File

@@ -4,31 +4,6 @@
<application>
<activity
android:name="cc.winboll.studio.powerbell.MainActivity"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="cc.winboll.studio.powerbell.unittest.MainUnitTestActivity"
android:exported="true">
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.powerbell.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
</application>
</manifest>