Compare commits
68 Commits
timestamp-
...
aes-v15.6.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8190f4818f | ||
![]() |
f339733d4b | ||
![]() |
0a993ccb9b | ||
![]() |
9bd4c41b75 | ||
![]() |
42256fd0e4 | ||
![]() |
2a11205468 | ||
![]() |
bb9f46373a | ||
![]() |
af797743d4 | ||
![]() |
1b37f1bd11 | ||
![]() |
00177284c1 | ||
![]() |
178e00f8b4 | ||
![]() |
743a5d5299 | ||
![]() |
30f75f2394 | ||
![]() |
9d66934694 | ||
![]() |
c7177f5fa5 | ||
![]() |
9e8263ae8f | ||
![]() |
5ee9da1982 | ||
![]() |
f59f7676b7 | ||
![]() |
19b8251182 | ||
![]() |
4dadb15c0c | ||
![]() |
21b5487513 | ||
![]() |
f49c8141a8 | ||
![]() |
6f3ce092ca | ||
![]() |
29785be88d | ||
![]() |
bf0a70e056 | ||
![]() |
3f809e4c7a | ||
![]() |
9c3c485ecd | ||
![]() |
336511931d | ||
![]() |
d3da3304ee | ||
![]() |
a5295a0744 | ||
![]() |
9b761b35ce | ||
![]() |
5206f8781e | ||
![]() |
2c0a5bfe65 | ||
![]() |
49bce0f773 | ||
![]() |
07c6778212 | ||
![]() |
1cdad9ac7d | ||
![]() |
986922c1fa | ||
![]() |
bff40d2a64 | ||
![]() |
13e0ad3f03 | ||
![]() |
be85ef923e | ||
![]() |
9a3383a43b | ||
![]() |
b44e5575b3 | ||
![]() |
4a4e20a6bd | ||
![]() |
4c0d825d7b | ||
![]() |
ae111b3a99 | ||
![]() |
640f0c39e9 | ||
![]() |
38e4deb425 | ||
![]() |
d06023d023 | ||
![]() |
cb486bb90c | ||
![]() |
2cbf9e5669 | ||
![]() |
1a7134711e | ||
![]() |
dabd1271fd | ||
![]() |
0d3d691d8a | ||
![]() |
f3198de003 | ||
![]() |
df179242f0 | ||
![]() |
1ae3fdf2b7 | ||
![]() |
7edc341f15 | ||
![]() |
f1669f9e41 | ||
![]() |
63b1d2dece | ||
![]() |
c302179960 | ||
![]() |
df55608eb3 | ||
![]() |
b64a229ab1 | ||
![]() |
d507d4ac06 | ||
![]() |
e815c08970 | ||
![]() |
d780116031 | ||
![]() |
5714aed761 | ||
![]() |
f04f5cf77c | ||
![]() |
6ccbaf8c32 |
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun May 04 06:42:28 GMT 2025
|
||||
stageCount=1
|
||||
#Mon May 12 18:10:02 HKT 2025
|
||||
stageCount=3
|
||||
libraryProject=libaes
|
||||
baseVersion=15.6
|
||||
publishVersion=15.6.0
|
||||
buildCount=10
|
||||
baseBetaVersion=15.6.1
|
||||
publishVersion=15.6.2
|
||||
buildCount=0
|
||||
baseBetaVersion=15.6.3
|
||||
|
@@ -1,81 +0,0 @@
|
||||
package cc.winboll.studio.appbase;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/03/28 15:34:16
|
||||
* @Describe 应用活动窗口基类
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.view.MenuItem;
|
||||
import cc.winboll.studio.appbase.App;
|
||||
import cc.winboll.studio.appbase.R;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.dialogs.YesNoAlertDialog;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libappbase.winboll.WinBoLLActivityManager;
|
||||
|
||||
public class WinBoLLActivityBase extends Activity implements IWinBoLLActivity {
|
||||
|
||||
public static final String TAG = "WinBoLLActivityBase";
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
WinBoLLActivityManager getWinBoLLActivityManager() {
|
||||
return WinBoLLActivityManager.getInstance(GlobalApplication.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWinBoLLActivityManager().add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
|
||||
super.onPostCreate(savedInstanceState, persistentState);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == cc.winboll.studio.appbase.R.id.item_log) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
|
||||
return true;
|
||||
} else if(item.getItemId() == cc.winboll.studio.appbase.R.id.item_minimal) {
|
||||
//moveTaskToBack(true);
|
||||
exit();
|
||||
}
|
||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
void exit() {
|
||||
YesNoAlertDialog.show(this, "Exit " + getString(R.string.app_name), "Close all activity and exit?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
|
||||
@Override
|
||||
public void onYes() {
|
||||
App.getWinBoLLActivityManager().finishAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
getWinBoLLActivityManager().registeRemove(this);
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
package cc.winboll.studio.apputils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
* @Date 2025/01/17 19:50:46
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.apputils.R;
|
||||
import cc.winboll.studio.libapputils.views.StringToQrCodeView;
|
||||
|
||||
public class TestStringToQRCodeViewActivity extends Activity {
|
||||
|
||||
public static final String TAG = "TestStringToQrCodeViewActivity";
|
||||
|
||||
StringToQrCodeView mStringToQrCodeView;
|
||||
//
|
||||
// @Override
|
||||
// public Activity getActivity() {
|
||||
// return this;
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_teststringtoqrcodeview);
|
||||
|
||||
// 初始化工具栏
|
||||
Toolbar mToolbar = findViewById(R.id.toolbar);
|
||||
mToolbar.setSubtitle(TAG);
|
||||
setActionBar(mToolbar);
|
||||
|
||||
mStringToQrCodeView = findViewById(R.id.activityteststringtoqrcodeviewStringToQrCodeView1);
|
||||
}
|
||||
}
|
@@ -19,7 +19,7 @@ def genVersionName(def versionName){
|
||||
|
||||
android {
|
||||
compileSdkVersion 32
|
||||
buildToolsVersion "33.0.3"
|
||||
buildToolsVersion "32.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "cc.winboll.studio.autoinstaller"
|
||||
@@ -29,7 +29,7 @@ android {
|
||||
// versionName 更新后需要手动设置
|
||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "5.0"
|
||||
versionName "15.2"
|
||||
if(true) {
|
||||
versionName = genVersionName("${versionName}")
|
||||
}
|
||||
@@ -41,30 +41,33 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'cc.winboll.studio:winboll-shared:1.1.0'
|
||||
api 'io.github.medyo:android-about-page:2.0.0'
|
||||
api 'com.github.getActivity:ToastUtils:10.5'
|
||||
api 'com.jcraft:jsch:0.1.55'
|
||||
api 'org.jsoup:jsoup:1.13.1'
|
||||
api 'com.squareup.okhttp3:okhttp:4.4.1'
|
||||
|
||||
api 'androidx.appcompat:appcompat:1.0.0'
|
||||
api 'androidx.fragment:fragment:1.0.0'
|
||||
api 'com.google.android.material:material:1.0.0'
|
||||
|
||||
api 'androidx.vectordrawable:vectordrawable-animated:1.0.0'
|
||||
api 'androidx.lifecycle:lifecycle-livedata:1.0.0'
|
||||
|
||||
//api 'cc.winboll.studio:libaes:6.3.2'
|
||||
//api 'cc.winboll.studio:libapputils:8.3.8'
|
||||
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
api 'cc.winboll.studio:libaes:15.6.0'
|
||||
api 'cc.winboll.studio:libapputils:15.3.4'
|
||||
api 'cc.winboll.studio:libappbase:15.7.6'
|
||||
|
||||
// SSH
|
||||
api 'com.jcraft:jsch:0.1.55'
|
||||
// Html 解析
|
||||
api 'org.jsoup:jsoup:1.13.1'
|
||||
// 二维码类库
|
||||
api 'com.google.zxing:core:3.4.1'
|
||||
api 'com.journeyapps:zxing-android-embedded:3.6.0'
|
||||
// 应用介绍页类库
|
||||
api 'io.github.medyo:android-about-page:2.0.0'
|
||||
// 吐司类库
|
||||
api 'com.github.getActivity:ToastUtils:10.5'
|
||||
// 网络连接类库
|
||||
api 'com.squareup.okhttp3:okhttp:4.4.1'
|
||||
// AndroidX 类库
|
||||
api 'androidx.appcompat:appcompat:1.1.0'
|
||||
api 'com.google.android.material:material:1.4.0'
|
||||
//api 'androidx.viewpager:viewpager:1.0.0'
|
||||
//api 'androidx.vectordrawable:vectordrawable:1.1.0'
|
||||
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
|
||||
//api 'androidx.fragment:fragment:1.1.0'
|
||||
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Thu Jan 02 02:39:56 HKT 2025
|
||||
stageCount=4
|
||||
#Sun May 04 05:32:00 GMT 2025
|
||||
stageCount=1
|
||||
libraryProject=
|
||||
baseVersion=5.0
|
||||
publishVersion=5.0.3
|
||||
buildCount=0
|
||||
baseBetaVersion=5.0.4
|
||||
baseVersion=15.2
|
||||
publishVersion=15.2.0
|
||||
buildCount=74
|
||||
baseBetaVersion=15.2.1
|
||||
|
@@ -23,6 +23,8 @@
|
||||
|
||||
<!-- MANAGE_EXTERNAL_STORAGE -->
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
@@ -33,7 +35,7 @@
|
||||
android:persistent="true"
|
||||
android:supportsRtl="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:theme="@style/Theme.Application">
|
||||
android:theme="@style/MyAppTheme">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
@@ -54,6 +56,21 @@
|
||||
|
||||
<service android:name=".services.AssistantService"/>
|
||||
|
||||
<service
|
||||
android:name=".AppBaseTileService"
|
||||
android:exported="true"
|
||||
android:label="@string/tileservice_name"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
|
||||
<intent-filter>
|
||||
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE"/>
|
||||
|
||||
</intent-filter>
|
||||
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.MainReceiver"
|
||||
android:enabled="true"
|
||||
|
@@ -6,22 +6,23 @@ package cc.winboll.studio.autoinstaller;
|
||||
* @Describe 全局应用类
|
||||
*/
|
||||
import android.view.Gravity;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import cc.winboll.studio.shared.app.WinBollApplication;
|
||||
import com.hjq.toast.style.WhiteToastStyle;
|
||||
|
||||
public class App extends WinBollApplication {
|
||||
public class App extends GlobalApplication {
|
||||
|
||||
public static final String TAG = "App";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
setIsDebug(BuildConfig.DEBUG);
|
||||
|
||||
// 初始化 Toast 框架
|
||||
ToastUtils.init(this);
|
||||
// 设置 Toast 布局样式
|
||||
ToastUtils.setView(R.layout.toast_custom_view);
|
||||
//ToastUtils.setStyle(new WhiteToastStyle());
|
||||
//ToastUtils.setView(R.layout.view_toast);
|
||||
ToastUtils.setStyle(new WhiteToastStyle());
|
||||
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,12 @@
|
||||
package cc.winboll.studio.autoinstaller;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/04/15 09:24:46
|
||||
* @Describe 磁贴工具服务类
|
||||
*/
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
import cc.winboll.studio.autoinstaller.models.AppConfigs;
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package cc.winboll.studio.autoinstaller;
|
||||
|
||||
import android.os.FileObserver;
|
||||
import cc.winboll.studio.shared.log.LogUtils;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class FileListener extends FileObserver {
|
||||
public final static String TAG = "FileListener";
|
||||
|
@@ -6,21 +6,24 @@ import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.SimpleAdapter;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextClock;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.content.FileProvider;
|
||||
import cc.winboll.studio.shared.log.LogUtils;
|
||||
import cc.winboll.studio.shared.log.LogView;
|
||||
import cc.winboll.studio.autoinstaller.MainActivity;
|
||||
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.models.APKModel;
|
||||
import cc.winboll.studio.autoinstaller.models.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.services.MainService;
|
||||
import cc.winboll.studio.autoinstaller.utils.NotificationUtil;
|
||||
import cc.winboll.studio.autoinstaller.utils.PackageUtil;
|
||||
import cc.winboll.studio.autoinstaller.views.ListViewForScrollView;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.LogView;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@@ -30,8 +33,13 @@ import java.util.Map;
|
||||
public class MainActivity extends Activity {
|
||||
public static final String TAG = "MainActivity";
|
||||
|
||||
public static final int MSG_UPDATE_STATUS = 0;
|
||||
|
||||
private static final int INSTALL_PERMISSION_CODE = 1;
|
||||
|
||||
static MainActivity _MainActivity;
|
||||
|
||||
ArrayList<APKModel> _APKModelList = new ArrayList<APKModel>();
|
||||
LogView mLogView;
|
||||
TextClock mTextClock;
|
||||
EditText mEditText;
|
||||
@@ -60,9 +68,11 @@ public class MainActivity extends Activity {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
_MainActivity = this;
|
||||
initView();
|
||||
|
||||
if (getIntent().getAction().equals(ACTION_NEW_INSTALLTASK)) {
|
||||
String action = getIntent().getAction();
|
||||
if ((action != null) && action.equals(ACTION_NEW_INSTALLTASK)) {
|
||||
mszInstalledPackageName = getIntent().getStringExtra(EXTRA_INSTALLED_PACKAGENAME);
|
||||
mszInstalledAPKFilePath = getIntent().getStringExtra(EXTRA_INSTALLED_APKFILEPATH);
|
||||
installAPK();
|
||||
@@ -76,11 +86,7 @@ public class MainActivity extends Activity {
|
||||
mLogView = findViewById(R.id.logview);
|
||||
mLogView.start();
|
||||
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this);
|
||||
if (appConfigs == null) {
|
||||
appConfigs = new AppConfigs();
|
||||
AppConfigs.saveAppConfigs(this, appConfigs);
|
||||
}
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(this).loadAppConfigs(this);
|
||||
|
||||
if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) {
|
||||
((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(true);
|
||||
@@ -131,23 +137,44 @@ public class MainActivity extends Activity {
|
||||
|
||||
}
|
||||
|
||||
String getLastApkPackageName() {
|
||||
APKModel.loadBeanList(this, _APKModelList, APKModel.class);
|
||||
if (_APKModelList.size() > 0) {
|
||||
return _APKModelList.get(_APKModelList.size() - 1).getApkPackageName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void onOpenAPP(View view) {
|
||||
if (mszInstalledPackageName.trim().equals("")) {
|
||||
String szInstalledPackageName = getLastApkPackageName();
|
||||
LogUtils.d(TAG, "szInstalledPackageName : " + szInstalledPackageName);
|
||||
if (szInstalledPackageName.trim().equals("")) {
|
||||
ToastUtils.show("Installed APP package name is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = getPackageManager().getLaunchIntentForPackage(mszInstalledPackageName);
|
||||
if (intent != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
// 若没能获取到启动意图,可进行相应提示等操作,比如跳转到应用商店让用户下载该应用(示例)
|
||||
Intent marketIntent = new Intent(Intent.ACTION_VIEW);
|
||||
marketIntent.setData(Uri.parse("market://details?id=" + mszInstalledPackageName));
|
||||
startActivity(marketIntent);
|
||||
}
|
||||
PackageUtil.openAPP(this, szInstalledPackageName);
|
||||
}
|
||||
|
||||
// public void onOpenAPP(View view) {
|
||||
// String szInstalledPackageName = getLastApkPackageName();
|
||||
// if (szInstalledPackageName.trim().equals("")) {
|
||||
// ToastUtils.show("Installed APP package name is null.");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// Intent intent = getPackageManager().getLaunchIntentForPackage(mszInstalledPackageName);
|
||||
// if (intent != null) {
|
||||
// //ToastUtils.show("startActivity");
|
||||
// startActivity(intent);
|
||||
// } else {
|
||||
// // 若没能获取到启动意图,可进行相应提示等操作,比如跳转到应用商店让用户下载该应用(示例)
|
||||
// Intent marketIntent = new Intent(Intent.ACTION_VIEW);
|
||||
// marketIntent.setData(Uri.parse("market://details?id=" + mszInstalledPackageName));
|
||||
// startActivity(marketIntent);
|
||||
// }
|
||||
// }
|
||||
|
||||
public void onInstallAPK(View view) {
|
||||
installAPK();
|
||||
}
|
||||
@@ -185,17 +212,22 @@ public class MainActivity extends Activity {
|
||||
}
|
||||
|
||||
public void onLockPath(View view) {
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this);
|
||||
|
||||
Switch sw = (Switch)view;
|
||||
if (sw.isChecked()) {
|
||||
setMainServiceStatus(sw.isChecked());
|
||||
}
|
||||
|
||||
public void setMainServiceStatus(boolean isEnable) {
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(this).loadAppConfigs(this);
|
||||
|
||||
Switch sw = (Switch)findViewById(R.id.activitymainSwitch1);
|
||||
if (isEnable) {
|
||||
String szFilePath = mEditText.getText().toString();
|
||||
|
||||
// 设置空路径时退出
|
||||
//
|
||||
if (szFilePath.trim().equals("")) {
|
||||
sw.setChecked(false);
|
||||
Toast.makeText(getApplication(), "监控路径为空。", Toast.LENGTH_SHORT).show();
|
||||
ToastUtils.show("监控路径为空。");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -236,7 +268,7 @@ public class MainActivity extends Activity {
|
||||
stopWatchingFile();
|
||||
|
||||
}
|
||||
AppConfigs.saveAppConfigs(this, appConfigs);
|
||||
AppConfigs.getInstance(this).saveAppConfigs(this, appConfigs);
|
||||
}
|
||||
|
||||
void stopWatchingFile() {
|
||||
@@ -251,8 +283,6 @@ public class MainActivity extends Activity {
|
||||
Intent intentService = new Intent(MainActivity.this, MainService.class);
|
||||
//intentService.putExtra(MainService.EXTRA_APKFILEPATH, szAPKFilePath);
|
||||
startService(intentService);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -298,7 +328,7 @@ public class MainActivity extends Activity {
|
||||
}*/
|
||||
|
||||
public void onChangeSetupMode(View view) {
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(this).loadAppConfigs(this);
|
||||
|
||||
if (view.getId() == R.id.activitymainRadioButton1) {
|
||||
appConfigs.setSetupMode(AppConfigs.SetupMode.WATCHOUTPUTINSTALLER);
|
||||
@@ -307,6 +337,42 @@ public class MainActivity extends Activity {
|
||||
appConfigs.setSetupMode(AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO);
|
||||
((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(false);
|
||||
}
|
||||
AppConfigs.saveAppConfigs(this, appConfigs);
|
||||
AppConfigs.getInstance(this).saveAppConfigs(this, appConfigs);
|
||||
}
|
||||
|
||||
// 定义Handler
|
||||
static Handler _Handler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
if (msg.what == MSG_UPDATE_STATUS) {
|
||||
if (_MainActivity != null) {
|
||||
boolean isEnableMainService = (boolean)msg.obj;
|
||||
// 处理消息,这里更新 MainService 的状态
|
||||
_MainActivity.setMainServiceStatus(isEnableMainService);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void updateMainServiceStatus(boolean isEnable) {
|
||||
if (_Handler != null) {
|
||||
Message msg = new Message();
|
||||
msg.obj = isEnable;
|
||||
msg.what = MSG_UPDATE_STATUS;
|
||||
_Handler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopMainService() {
|
||||
if (_MainActivity != null && _Handler != null) {
|
||||
updateMainServiceStatus(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void startMainService() {
|
||||
if (_MainActivity != null && _Handler != null) {
|
||||
updateMainServiceStatus(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,75 @@
|
||||
package cc.winboll.studio.autoinstaller.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/04/02 20:50:29
|
||||
* @Describe 监控的 APK 安装文件对应的应用信息数据模型
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
public class APKModel extends BaseBean {
|
||||
|
||||
public static final String TAG = "APPModel";
|
||||
|
||||
// 每次更新的 APK 文件对应的应用包名称
|
||||
String apkPackageName;
|
||||
|
||||
public APKModel() {
|
||||
this.apkPackageName = "";
|
||||
}
|
||||
|
||||
public APKModel(String apkPackageName) {
|
||||
this.apkPackageName = apkPackageName;
|
||||
}
|
||||
|
||||
public void setApkPackageName(String apkPackageName) {
|
||||
this.apkPackageName = apkPackageName;
|
||||
}
|
||||
|
||||
public String getApkPackageName() {
|
||||
return apkPackageName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return APKModel.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("appPackageName").value(getApkPackageName());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("appPackageName")) {
|
||||
setApkPackageName(jsonReader.nextString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.autoinstaller.beans;
|
||||
package cc.winboll.studio.autoinstaller.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
@@ -8,13 +8,14 @@ package cc.winboll.studio.autoinstaller.beans;
|
||||
import android.content.Context;
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.models.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.utils.FileUtil;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import cc.winboll.studio.shared.log.LogUtils;
|
||||
|
||||
public class AppConfigs implements Serializable {
|
||||
|
||||
@@ -26,6 +27,21 @@ public class AppConfigs implements Serializable {
|
||||
NEWAPKINFONEWAPKINFO // 调用[应用信息查看器]打开应用包
|
||||
};
|
||||
|
||||
static volatile AppConfigs _AppConfigs;
|
||||
Context mContext;
|
||||
|
||||
AppConfigs(Context context) {
|
||||
mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
public static synchronized AppConfigs getInstance(Context context) {
|
||||
if (_AppConfigs == null) {
|
||||
_AppConfigs = new AppConfigs(context);
|
||||
_AppConfigs.loadAppConfigs(_AppConfigs.mContext);
|
||||
}
|
||||
return _AppConfigs;
|
||||
}
|
||||
|
||||
// 监控文件路径
|
||||
private String watchingFilePath = "";
|
||||
|
||||
@@ -85,8 +101,8 @@ public class AppConfigs implements Serializable {
|
||||
return "";
|
||||
}
|
||||
|
||||
public static AppConfigs parseAppConfigs(String szAppConfigs) {
|
||||
AppConfigs appConfigs = new AppConfigs();
|
||||
public AppConfigs parseAppConfigs(String szAppConfigs) {
|
||||
AppConfigs appConfigs = new AppConfigs(mContext);
|
||||
// 创建 JsonWriter 对象
|
||||
StringReader stringReader = new StringReader(szAppConfigs);
|
||||
JsonReader jsonReader = new
|
||||
@@ -121,20 +137,35 @@ public class AppConfigs implements Serializable {
|
||||
static String getDataPath(Context context) {
|
||||
return context.getExternalFilesDir(TAG) + "/" + TAG + ".json";
|
||||
}
|
||||
|
||||
public static AppConfigs loadAppConfigs(Context context) {
|
||||
|
||||
public AppConfigs loadAppConfigs() {
|
||||
AppConfigs appConfigs = null;
|
||||
try {
|
||||
String szJson = FileUtil.readFile(getDataPath(context));
|
||||
appConfigs = AppConfigs.parseAppConfigs(szJson);
|
||||
String szJson = FileUtil.readFile(getDataPath(mContext));
|
||||
appConfigs = AppConfigs.getInstance(mContext).parseAppConfigs(szJson);
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return appConfigs;
|
||||
}
|
||||
|
||||
public static void saveAppConfigs(Context context, AppConfigs appConfigs) {
|
||||
public AppConfigs loadAppConfigs(Context context) {
|
||||
AppConfigs appConfigs = null;
|
||||
try {
|
||||
String szJson = FileUtil.readFile(getDataPath(context.getApplicationContext()));
|
||||
appConfigs = AppConfigs.getInstance(mContext).parseAppConfigs(szJson);
|
||||
if(appConfigs != null) {
|
||||
_AppConfigs = appConfigs;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
return _AppConfigs;
|
||||
}
|
||||
|
||||
public void saveAppConfigs(Context context, AppConfigs appConfigs) {
|
||||
try {
|
||||
ToastUtils.show(String.format("AppConfigs set enable service to %s", appConfigs.isEnableService()));
|
||||
//LogUtils.d(TAG, "appConfigs is : " + appConfigs.toString());
|
||||
String szJson = appConfigs.toString();
|
||||
FileUtil.writeFile(getDataPath(context), szJson);
|
||||
@@ -142,4 +173,8 @@ public class AppConfigs implements Serializable {
|
||||
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveAppConfigs() {
|
||||
saveAppConfigs(mContext, this);
|
||||
}
|
||||
}
|
@@ -1,54 +1,49 @@
|
||||
package cc.winboll.studio.appbase.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/02/17 10:05:09
|
||||
* @Describe APPSOSReportBean
|
||||
*/
|
||||
package cc.winboll.studio.autoinstaller.models;
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
public class WinBoLLNewsBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "WinBoLLNewsBean";
|
||||
|
||||
protected String message;
|
||||
|
||||
public WinBoLLNewsBean() {
|
||||
this.message = "";
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/04/15 09:27:39
|
||||
* @Describe MainServiceBean
|
||||
*/
|
||||
public class MainServiceBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "MainServiceBean";
|
||||
|
||||
boolean isEnable;
|
||||
|
||||
public MainServiceBean() {
|
||||
this.isEnable = false;
|
||||
}
|
||||
|
||||
public WinBoLLNewsBean(String message) {
|
||||
this.message = message;
|
||||
public void setIsEnable(boolean isEnable) {
|
||||
this.isEnable = isEnable;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
public boolean isEnable() {
|
||||
return isEnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return WinBoLLNewsBean.class.getName();
|
||||
return MainServiceBean.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("message").value(getMessage());
|
||||
jsonWriter.name("isEnable").value(isEnable());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("message")) {
|
||||
setMessage(jsonReader.nextString());
|
||||
if (name.equals("isEnable")) {
|
||||
setIsEnable(jsonReader.nextBoolean());
|
||||
} else {
|
||||
return false;
|
||||
}
|
@@ -4,9 +4,9 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.models.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.services.MainService;
|
||||
import cc.winboll.studio.shared.log.LogUtils;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@QQ.COM
|
||||
@@ -23,7 +23,7 @@ public class MainReceiver extends BroadcastReceiver {
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String szAction = intent.getAction();
|
||||
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(context);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(context).loadAppConfigs(context);
|
||||
if (appConfigs.isEnableService()) {
|
||||
Intent intentService = new Intent(context, MainService.class);
|
||||
//intentService.putExtra(MainService.EXTRA_APKFILEPATH, appConfigs.getWatchingFilePath());
|
||||
|
@@ -6,7 +6,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.models.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ public class AssistantService extends Service {
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
//LogUtils.d(TAG, "call onStartCommand(...)");
|
||||
run();
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs(AssistantService.this);
|
||||
return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class AssistantService extends Service {
|
||||
//
|
||||
void run() {
|
||||
//LogUtils.d(TAG, "call run()");
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs(AssistantService.this);
|
||||
if (appConfigs.isEnableService()) {
|
||||
if (mIsThreadAlive == false) {
|
||||
// 设置运行状态
|
||||
@@ -101,7 +101,7 @@ public class AssistantService extends Service {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
//LogUtils.d(TAG, "call onServiceDisconnected(...)");
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs(AssistantService.this);
|
||||
if (appConfigs.isEnableService()) {
|
||||
wakeupAndBindMain();
|
||||
}
|
||||
|
@@ -10,25 +10,32 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.content.FileProvider;
|
||||
import cc.winboll.studio.shared.log.LogUtils;
|
||||
import cc.winboll.studio.autoinstaller.FileListener;
|
||||
import cc.winboll.studio.autoinstaller.MainActivity;
|
||||
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.R;
|
||||
import cc.winboll.studio.autoinstaller.models.APKModel;
|
||||
import cc.winboll.studio.autoinstaller.models.AppConfigs;
|
||||
import cc.winboll.studio.autoinstaller.services.AssistantService;
|
||||
import cc.winboll.studio.autoinstaller.services.MainService;
|
||||
import cc.winboll.studio.autoinstaller.utils.NotificationUtil;
|
||||
import cc.winboll.studio.autoinstaller.utils.PackageUtil;
|
||||
import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class MainService extends Service {
|
||||
|
||||
public static String TAG = "MainService";
|
||||
|
||||
Context mContext;
|
||||
|
||||
ArrayList<APKModel> _APKModelList = new ArrayList<APKModel>();
|
||||
private static boolean _mIsServiceAlive;
|
||||
//String mszAPKFilePath;
|
||||
//String mszAPKFileName;
|
||||
@@ -47,6 +54,7 @@ public class MainService extends Service {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mContext = this;
|
||||
LogUtils.d(TAG, "onCreate()");
|
||||
_mIsServiceAlive = false;
|
||||
mHandler = new MyHandler(MainService.this);
|
||||
@@ -55,11 +63,14 @@ public class MainService extends Service {
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
// 初始化磁贴工具服务
|
||||
MainTileService mainTileService = new MainTileService(this);
|
||||
}
|
||||
|
||||
private void run() {
|
||||
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs(MainService.this);
|
||||
if (appConfigs.isEnableService()) {
|
||||
if (_mIsServiceAlive == false) {
|
||||
// 设置运行状态
|
||||
@@ -74,7 +85,8 @@ public class MainService extends Service {
|
||||
|
||||
startWatchingFile(appConfigs.getWatchingFilePath());
|
||||
|
||||
LogUtils.d(TAG, "running...");
|
||||
//LogUtils.d(TAG, "running...");
|
||||
//ToastUtils.show("running...");
|
||||
|
||||
} else {
|
||||
LogUtils.d(TAG, "_mIsServiceAlive is " + Boolean.toString(_mIsServiceAlive));
|
||||
@@ -94,6 +106,7 @@ public class MainService extends Service {
|
||||
}
|
||||
_mIsServiceAlive = false;
|
||||
LogUtils.d(TAG, "onDestroy()");
|
||||
mContext = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +114,7 @@ public class MainService extends Service {
|
||||
LogUtils.d(TAG, "onStartCommand");
|
||||
|
||||
run();
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs(MainService.this);
|
||||
|
||||
return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
@@ -118,7 +131,7 @@ public class MainService extends Service {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
//LogUtils.d(TAG, "call onServiceConnected(...)");
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs(MainService.this);
|
||||
if (appConfigs.isEnableService()) {
|
||||
// 唤醒守护进程
|
||||
wakeupAndBindAssistant();
|
||||
@@ -160,7 +173,7 @@ public class MainService extends Service {
|
||||
|
||||
});
|
||||
mFileListener.startWatching();
|
||||
ToastUtils.show("Start watching.");
|
||||
//ToastUtils.show("Start watching.");
|
||||
} else {
|
||||
// 父级文件夹不存在,就提示用户
|
||||
Toast.makeText(getApplication(), fParentDir.toString() + " no exist.", Toast.LENGTH_SHORT).show();
|
||||
@@ -176,6 +189,9 @@ public class MainService extends Service {
|
||||
// 调用[应用信息查看器]打开应用包
|
||||
//
|
||||
private void installAPK(String szAPKFilePath) {
|
||||
String szAPKPackageName = PackageUtil.getPackageNameFromApk(this, szAPKFilePath);
|
||||
saveAPKInfo(szAPKPackageName);
|
||||
|
||||
long nTimeNow = System.currentTimeMillis();
|
||||
/*SimpleDateFormat dateFormat = new SimpleDateFormat(
|
||||
"yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
@@ -210,15 +226,24 @@ public class MainService extends Service {
|
||||
//
|
||||
void installAPK2(String szAPKFilePath) {
|
||||
LogUtils.d(TAG, "installAPK2()");
|
||||
String szAPKPackageName = PackageUtil.getPackageNameFromApk(this, szAPKFilePath);
|
||||
saveAPKInfo(szAPKPackageName);
|
||||
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.setAction(MainActivity.ACTION_NEW_INSTALLTASK);
|
||||
intent.putExtra(MainActivity.EXTRA_INSTALLED_PACKAGENAME, PackageUtil.getPackageNameFromApk(this, szAPKFilePath));
|
||||
intent.putExtra(MainActivity.EXTRA_INSTALLED_PACKAGENAME, szAPKPackageName);
|
||||
intent.putExtra(MainActivity.EXTRA_INSTALLED_APKFILEPATH, szAPKFilePath);
|
||||
// Intent.ACTION_VIEW);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
this.startActivity(intent);
|
||||
}
|
||||
|
||||
void saveAPKInfo(String szApkPackageName) {
|
||||
APKModel.loadBeanList(this, _APKModelList, APKModel.class);
|
||||
_APKModelList.add(new APKModel(szApkPackageName));
|
||||
APKModel.saveBeanList(this, _APKModelList, APKModel.class);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
static class MyHandler extends Handler {
|
||||
@@ -231,7 +256,7 @@ public class MainService extends Service {
|
||||
switch (message.what) {
|
||||
case MSG_INSTALL_APK:
|
||||
{
|
||||
AppConfigs appConfigs = AppConfigs.loadAppConfigs(theActivity);
|
||||
AppConfigs appConfigs = AppConfigs.getInstance(theActivity).loadAppConfigs(theActivity);
|
||||
if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) {
|
||||
theActivity.installAPK2((String)message.obj);
|
||||
} else if (appConfigs.getSetupMode() == AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO) {
|
||||
@@ -245,4 +270,21 @@ public class MainService extends Service {
|
||||
super.handleMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
static class MainTileService extends TileService {
|
||||
Context mContext;
|
||||
MainTileService(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
super.onStartListening();
|
||||
Tile tile = getQsTile();
|
||||
tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_android));
|
||||
// 更新磁贴状态
|
||||
tile.setState((mContext == null || mContext.getApplicationContext() == null )?Tile.STATE_INACTIVE: Tile.STATE_ACTIVE);
|
||||
tile.updateTile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -55,8 +55,9 @@ public class NotificationUtil {
|
||||
|
||||
Notification mForegroundNotification = new Notification.Builder(service, szServiceChannelID)
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle(service.getString(R.string.app_name))
|
||||
.setContentText(service.TAG + " is started.")
|
||||
//.setContentTitle(service.getString(R.string.app_name))
|
||||
.setContentTitle(service.TAG + " is started.")
|
||||
//.setContentText(service.TAG + " is started.")
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
//设置红色
|
||||
|
@@ -6,9 +6,11 @@ package cc.winboll.studio.autoinstaller.utils;
|
||||
* @Describe 一个获取安卓APK安装文件的应用包名的函数
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import cc.winboll.studio.shared.log.LogUtils;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class PackageUtil {
|
||||
|
||||
@@ -27,4 +29,20 @@ public class PackageUtil {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void openAPP(Context context, String packageName) {
|
||||
// 这里假设要打开微信,微信的包名是com.tencent.mm
|
||||
//String packageName = "com.tencent.mm";
|
||||
LogUtils.d(TAG, "packageName : " + packageName);
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
Intent intent = packageManager.getLaunchIntentForPackage(packageName);
|
||||
if (intent != null) {
|
||||
ResolveInfo resolveInfo = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
if (resolveInfo != null) {
|
||||
LogUtils.d(TAG, "startActivity...");
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
11
autoinstaller/src/main/res/drawable/ic_android.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
|
||||
|
||||
</vector>
|
11
autoinstaller/src/main/res/drawable/ic_cloud.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20Z"/>
|
||||
|
||||
</vector>
|
11
autoinstaller/src/main/res/drawable/ic_cloud_outline.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20M6.5,18H18.5Q19.55,18 20.27,17.27 21,16.55 21,15.5 21,14.45 20.27,13.73 19.55,13 18.5,13H17V11Q17,8.93 15.54,7.46 14.08,6 12,6 9.93,6 8.46,7.46 7,8.93 7,11H6.5Q5.05,11 4.03,12.03 3,13.05 3,14.5 3,15.95 4.03,17 5.05,18 6.5,18M12,12Z"/>
|
||||
|
||||
</vector>
|
@@ -132,7 +132,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<cc.winboll.studio.shared.log.LogView
|
||||
<cc.winboll.studio.libappbase.LogView
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">AutoInstaller</string>
|
||||
<string name="tileservice_name">AutoInstaller</string>
|
||||
</resources>
|
||||
|
13
autoinstaller/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="MyAppTheme" parent="APPBaseTheme">
|
||||
<item name="attrColorPrimary">@color/colorPrimary</item>
|
||||
<item name="themeGlobalCrashActivity">@style/MyGlobalCrashActivityTheme</item>
|
||||
</style>
|
||||
|
||||
<style name="MyGlobalCrashActivityTheme" parent="GlobalCrashActivityTheme">
|
||||
<item name="colorTittle">#FFFFFFFF</item>
|
||||
<item name="colorTittleBackgound">#FF00A4B3</item>
|
||||
<item name="colorText">#FFFFFFFF</item>
|
||||
<item name="colorTextBackgound">#FF000000</item>
|
||||
</style>
|
||||
</resources>
|
@@ -1,14 +0,0 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Application" parent="android:Theme.Material.Light.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="android:colorPrimary">@color/purple_500</item>
|
||||
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="android:colorSecondary">@color/teal_200</item>
|
||||
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">@color/purple_700</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
31
build.gradle
@@ -1,11 +1,10 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
// 本地 Maven 仓库(默认路径为 ~/.m2/repository)
|
||||
//mavenLocal()
|
||||
// 或自定义本地仓库路径
|
||||
maven { url "file:///sdcard/.m2/repository" }
|
||||
|
||||
mavenLocal {
|
||||
// 设置本地Maven仓库路径
|
||||
url 'file:///sdcard/.m2/repository/'
|
||||
}
|
||||
// Nexus Maven 库地址
|
||||
// "WinBoLL Release"
|
||||
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
||||
@@ -20,7 +19,13 @@ buildscript {
|
||||
maven { url "https://jitpack.io" }
|
||||
mavenCentral()
|
||||
google()
|
||||
//println "mavenLocal : ==========="
|
||||
//println mavenLocal().url
|
||||
//println "mavenLocal : ==========="
|
||||
//mavenLocal()
|
||||
|
||||
// WinBoLL.CC 紧急备用 Maven 仓库
|
||||
maven { url 'https://spare-maven.winboll.cc/repository/' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.1' // 对应 compileSdkVersion 32
|
||||
@@ -31,10 +36,10 @@ buildscript {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
// 本地 Maven 仓库(默认路径为 ~/.m2/repository)
|
||||
//mavenLocal()
|
||||
// 或自定义本地仓库路径
|
||||
maven { url "file:///sdcard/.m2/repository" }
|
||||
mavenLocal {
|
||||
// 设置本地Maven仓库路径
|
||||
url 'file:///sdcard/.m2/repository/'
|
||||
}
|
||||
|
||||
// Nexus Maven 库地址
|
||||
// "WinBoLL Release"
|
||||
@@ -50,7 +55,13 @@ allprojects {
|
||||
maven { url "https://jitpack.io" }
|
||||
mavenCentral()
|
||||
google()
|
||||
mavenLocal()
|
||||
//println "mavenLocal : ==========="
|
||||
//println mavenLocal().url
|
||||
//println "mavenLocal : ==========="
|
||||
//mavenLocal()
|
||||
|
||||
// WinBoLL.CC 紧急备用 Maven 仓库
|
||||
maven { url 'https://spare-maven.winboll.cc/repository/' }
|
||||
}
|
||||
ext {
|
||||
// 定义全局变量,常用于版本管理
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun May 04 06:42:28 GMT 2025
|
||||
stageCount=1
|
||||
#Mon May 12 18:10:02 HKT 2025
|
||||
stageCount=3
|
||||
libraryProject=libaes
|
||||
baseVersion=15.6
|
||||
publishVersion=15.6.0
|
||||
buildCount=10
|
||||
baseBetaVersion=15.6.1
|
||||
publishVersion=15.6.2
|
||||
buildCount=0
|
||||
baseBetaVersion=15.6.3
|
||||
|
@@ -1,39 +0,0 @@
|
||||
package cc.winboll.studio.libappbase.sos;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/03/02 09:54:28
|
||||
* @Describe WinBoLL 系列应用通用管理类
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
public class WinBoLL {
|
||||
|
||||
public static final String TAG = "WinBoLL";
|
||||
|
||||
public static final String ACTION_BIND = WinBoLL.class.getName() + ".ACTION_BIND";
|
||||
public static final String EXTRA_APPMODEL = "EXTRA_APPMODEL";
|
||||
|
||||
public static void bindToAPPBase(Context context, String appMainService) {
|
||||
LogUtils.d(TAG, "bindToAPPBase(...)");
|
||||
String toPackage = "cc.winboll.studio.appbase";
|
||||
startBind(context, toPackage, appMainService);
|
||||
}
|
||||
|
||||
public static void bindToAPPBaseBeta(Context context, String appMainService) {
|
||||
LogUtils.d(TAG, "bindToAPPBaseBeta(...)");
|
||||
String toPackage = "cc.winboll.studio.appbase.beta";
|
||||
startBind(context, toPackage, appMainService);
|
||||
}
|
||||
|
||||
static void startBind(Context context, String toPackage, String appMainService) {
|
||||
Intent intent = new Intent(ACTION_BIND);
|
||||
intent.putExtra(EXTRA_APPMODEL, (new APPModel(toPackage, appMainService)).toString());
|
||||
intent.setPackage(toPackage);
|
||||
LogUtils.d(TAG, String.format("ACTION_BIND :\nTo Package : %s\nAPP Main Service : %s", toPackage, appMainService));
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
package cc.winboll.studio.libappbase.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/03/24 08:23:40
|
||||
* @Describe WinBoLL 活动窗口通用接口
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
public interface IWinBoLLActivity {
|
||||
|
||||
public static final String TAG = "IWinBoLLActivity";
|
||||
|
||||
// 获取活动窗口
|
||||
abstract public Activity getActivity();
|
||||
abstract public String getTag();
|
||||
}
|
@@ -1,356 +0,0 @@
|
||||
package cc.winboll.studio.libappbase.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen@AliYun.Com
|
||||
* @Date 2025/03/24 08:25:43
|
||||
* @Describe 应用活动窗口管理器
|
||||
* 参考 :
|
||||
* android 类似微信小程序多任务窗口 及 设置 TaskDescription 修改 icon 和 label
|
||||
* https://blog.csdn.net/qq_29364417/article/details/109379915?app_version=6.4.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22109379915%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
|
||||
*/
|
||||
import android.app.ActivityManager;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class WinBoLLActivityManager {
|
||||
|
||||
public static final String TAG = "WinBoLLActivityManager";
|
||||
public static final String EXTRA_TAG = "EXTRA_TAG";
|
||||
|
||||
public static enum WinBoLLUI_TYPE {
|
||||
Aplication, // 退出应用后,保持最近任务栏任务记录主窗口
|
||||
Service // 退出应用后,清理所有最近任务栏任务记录窗口
|
||||
};
|
||||
|
||||
// 应用类型标志
|
||||
static volatile WinBoLLUI_TYPE _mWinBoLLUI_TYPE = WinBoLLUI_TYPE.Service;
|
||||
|
||||
GlobalApplication mGlobalApplication;
|
||||
static volatile WinBoLLActivityManager _Instance;
|
||||
static volatile Map<String, IWinBoLLActivity> _mapIWinBoLLList;
|
||||
volatile IWinBoLLActivity mFirstIWinBoLLActivity;
|
||||
|
||||
WinBoLLActivityManager(GlobalApplication application) {
|
||||
mGlobalApplication = application;
|
||||
_mapIWinBoLLList = new HashMap<String, IWinBoLLActivity>();
|
||||
}
|
||||
|
||||
public static synchronized WinBoLLActivityManager getInstance(GlobalApplication application) {
|
||||
LogUtils.d(TAG, "getInstance");
|
||||
if (_Instance == null) {
|
||||
LogUtils.d(TAG, "_Instance == null");
|
||||
_Instance = new WinBoLLActivityManager(application);
|
||||
}
|
||||
return _Instance;
|
||||
}
|
||||
|
||||
//
|
||||
// 设置 WinBoLL 应用 UI 类型
|
||||
//
|
||||
public synchronized static void setWinBoLLUI_TYPE(WinBoLLUI_TYPE mWinBoLLUI_TYPE) {
|
||||
_mWinBoLLUI_TYPE = mWinBoLLUI_TYPE;
|
||||
}
|
||||
|
||||
//
|
||||
// 获取 WinBoLL 应用 UI 类型
|
||||
//
|
||||
public synchronized static WinBoLLUI_TYPE getWinBoLLUI_TYPE() {
|
||||
return _mWinBoLLUI_TYPE;
|
||||
}
|
||||
|
||||
//
|
||||
// 把Activity添加到管理中
|
||||
//
|
||||
public <T extends IWinBoLLActivity> void add(T iWinBoLL) {
|
||||
String tag = ((IWinBoLLActivity)iWinBoLL).getTag();
|
||||
LogUtils.d(TAG, String.format("add(T iWinBoLL) tag is %s", tag));
|
||||
if (isActive(tag)) {
|
||||
LogUtils.d(TAG, String.format("isActive(tag) is true, tag : %s.", tag));
|
||||
} else {
|
||||
// 设置起始活动窗口,以便最后退出时提问
|
||||
if (mFirstIWinBoLLActivity == null && _mapIWinBoLLList.size() == 0) {
|
||||
LogUtils.d(TAG, "Set firstIWinBoLLActivity, iWinBoLL.getTag() is %s" + iWinBoLL.getTag());
|
||||
mFirstIWinBoLLActivity = iWinBoLL;
|
||||
}
|
||||
|
||||
// 添加到活动窗口列表
|
||||
_mapIWinBoLLList.put(iWinBoLL.getTag(), iWinBoLL);
|
||||
LogUtils.d(TAG, String.format("Add activity : %s\n_mapActivityList.size() : %d", iWinBoLL.getTag(), _mapIWinBoLLList.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// activity: 为 null 时,
|
||||
// intent.putExtra 函数 EXTRA_TAG 参数为 tag
|
||||
// activity: 不为 null 时,
|
||||
// intent.putExtra 函数 "tag" 参数为 activity.getTag()
|
||||
//
|
||||
public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Class<T> clazz) {
|
||||
try {
|
||||
// 如果窗口已存在就重启窗口
|
||||
String tag = ((IWinBoLLActivity)clazz.newInstance()).getTag();
|
||||
LogUtils.d(TAG, String.format("startWinBoLLActivity(Context context, Class<T> clazz) tag is %s", tag));
|
||||
if (isActive(tag)) {
|
||||
resumeActivity(context, tag);
|
||||
return;
|
||||
}
|
||||
//ToastUtils.show("startWinBoLLActivity(Context context, Class<T> clazz)");
|
||||
|
||||
// 新建一个任务窗口
|
||||
Intent intent = new Intent(context, clazz);
|
||||
//打开多任务窗口 flags
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(EXTRA_TAG, tag);
|
||||
context.startActivity(intent);
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends IWinBoLLActivity> void startWinBoLLActivity(Context context, Intent intent, Class<T> clazz) {
|
||||
try {
|
||||
// 如果窗口已存在就重启窗口
|
||||
String tag = ((IWinBoLLActivity)clazz.newInstance()).getTag();
|
||||
LogUtils.d(TAG, String.format("startWinBoLLActivity(Context context, Intent intent, Class<T> clazz) tag is %s", tag));
|
||||
if (isActive(tag)) {
|
||||
resumeActivity(context, tag);
|
||||
return;
|
||||
}
|
||||
|
||||
// 新建一个任务窗口
|
||||
//Intent intent = new Intent(context, clazz);
|
||||
//打开多任务窗口 flags
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(EXTRA_TAG, tag);
|
||||
context.startActivity(intent);
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends IWinBoLLActivity> void startLogActivity(Context context) {
|
||||
// 如果窗口已存在就重启窗口
|
||||
String tag = LogActivity.TAG;
|
||||
if (isActive(tag)) {
|
||||
resumeActivity(context, tag);
|
||||
return;
|
||||
}
|
||||
|
||||
// 新建一个任务窗口
|
||||
Intent intent = new Intent(context, LogActivity.class);
|
||||
//打开多任务窗口 flags
|
||||
// Define the bounds.
|
||||
// Rect bounds = new Rect(0, 0, 800, 200);
|
||||
// // Set the bounds as an activity option.
|
||||
// ActivityOptions options = ActivityOptions.makeBasic();
|
||||
// options.setLaunchBounds(bounds);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
|
||||
intent.putExtra(EXTRA_TAG, tag);
|
||||
|
||||
//context.startActivity(intent, options.toBundle());
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public boolean isFirstIWinBoLLActivity(IWinBoLLActivity iWinBoLLActivity) {
|
||||
return mFirstIWinBoLLActivity != null && mFirstIWinBoLLActivity == iWinBoLLActivity;
|
||||
}
|
||||
|
||||
//
|
||||
// 判断 tag绑定的 MyActivity是否存在
|
||||
//
|
||||
public boolean isActive(String tag) {
|
||||
LogUtils.d(TAG, String.format("isActive(String tag) tag is %s", tag));
|
||||
//printIWinBoLLListInfo();
|
||||
IWinBoLLActivity iWinBoLL = getIWinBoLL(tag);
|
||||
if (iWinBoLL != null) {
|
||||
//LogUtils.d(TAG, "isActive(...) activity != null tag " + tag);
|
||||
//ToastUtils.show("activity != null tag " + tag);
|
||||
//判断是否为 BaseActivity,如果已经销毁,则移除
|
||||
if (iWinBoLL.getActivity().isFinishing() || iWinBoLL.getActivity().isDestroyed()) {
|
||||
_mapIWinBoLLList.remove(iWinBoLL.getTag());
|
||||
//_mWinBoLLActivityList.remove(activity);
|
||||
LogUtils.d(TAG, String.format("isActive(...) remove activity.\ntag : %s", tag));
|
||||
return false;
|
||||
} else {
|
||||
LogUtils.d(TAG, String.format("isActive(...) activity is exist.\ntag : %s", tag));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
LogUtils.d(TAG, String.format("isActive(...) iWinBoLL is null tag by %s", tag));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static IWinBoLLActivity getIWinBoLL(String tag) {
|
||||
LogUtils.d(TAG, String.format("getIWinBoLL(String tag) %s", tag));
|
||||
return _mapIWinBoLLList.get(tag);
|
||||
}
|
||||
|
||||
//
|
||||
// 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
|
||||
//
|
||||
public <T extends IWinBoLLActivity> void resumeActivity(Context context, String tag) {
|
||||
LogUtils.d(TAG, "resumeActivity(Context context, String tag)");
|
||||
T iWinBoLL = (T)getIWinBoLL(tag);
|
||||
LogUtils.d(TAG, String.format("iWinBoLL.getTag() %s", iWinBoLL.getTag()));
|
||||
//LogUtils.d(TAG, "activity " + activity.getTag());
|
||||
if (iWinBoLL != null && !iWinBoLL.getActivity().isFinishing() && !iWinBoLL.getActivity().isDestroyed()) {
|
||||
resumeActivity(context, iWinBoLL);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 找到tag 绑定的 BaseActivity ,通过 getTaskId() 移动到前台
|
||||
//
|
||||
public <T extends IWinBoLLActivity> void resumeActivity(Context context, T iWinBoLL) {
|
||||
LogUtils.d(TAG, "resumeActivity(Context context, T iWinBoLL)");
|
||||
ActivityManager am = (ActivityManager) mGlobalApplication.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
//返回启动它的根任务(home 或者 MainActivity)
|
||||
Intent intent = new Intent(mGlobalApplication, iWinBoLL.getClass());
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mGlobalApplication);
|
||||
stackBuilder.addNextIntentWithParentStack(intent);
|
||||
stackBuilder.startActivities();
|
||||
//moveTaskToFront(YourTaskId, 0);
|
||||
//ToastUtils.show("resumeActivity am.moveTaskToFront");
|
||||
LogUtils.d(TAG, String.format("iWinBoLL.getActivity().getTaskId() %d", iWinBoLL.getActivity().getTaskId()));
|
||||
am.moveTaskToFront(iWinBoLL.getActivity().getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
|
||||
LogUtils.d(TAG, "am.moveTaskToFront");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 结束所有 Activity
|
||||
//
|
||||
public void finishAll() {
|
||||
try {
|
||||
for (String key : _mapIWinBoLLList.keySet()) {
|
||||
//System.out.println("Key: " + key + ", Value: " + _mapActivityList.get(key));
|
||||
IWinBoLLActivity iWinBoLL = _mapIWinBoLLList.get(key);
|
||||
//ToastUtils.show("finishAll() activity");
|
||||
if (iWinBoLL != null && !iWinBoLL.getActivity().isFinishing() && !iWinBoLL.getActivity().isDestroyed()) {
|
||||
//ToastUtils.show("activity != null ...");
|
||||
if (getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Service) {
|
||||
// 结束窗口和最近任务栏, 建议前台服务类应用使用,可以方便用户再次调用 UI 操作。
|
||||
iWinBoLL.getActivity().finishAndRemoveTask();
|
||||
//ToastUtils.show("finishAll() activity.finishAndRemoveTask();");
|
||||
} else if (getWinBoLLUI_TYPE() == WinBoLLUI_TYPE.Aplication) {
|
||||
// 结束窗口保留最近任务栏,建议前台服务类应用使用,可以保持应用的系统自觉性。
|
||||
iWinBoLL.getActivity().finish();
|
||||
//ToastUtils.show("finishAll() activity.finish();");
|
||||
} else {
|
||||
LogUtils.d(TAG, "WinBoLLApplication.WinBoLLUI_TYPE error.");
|
||||
//ToastUtils.show("WinBoLLApplication.WinBoLLUI_TYPE error.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 结束指定Activity
|
||||
//
|
||||
public <T extends IWinBoLLActivity> void finish(T iWinBoLL) {
|
||||
try {
|
||||
if (iWinBoLL != null && !iWinBoLL.getActivity().isFinishing() && !iWinBoLL.getActivity().isDestroyed()) {
|
||||
//根据tag 移除 MyActivity
|
||||
//String tag= activity.getTag();
|
||||
//_mWinBoLLActivityList.remove(tag);
|
||||
//ToastUtils.show("remove");
|
||||
//ToastUtils.show("_mWinBoLLActivityArrayMap.size() " + Integer.toString(_mWinBoLLActivityArrayMap.size()));
|
||||
|
||||
// 窗口回调规则:
|
||||
// [] 当前窗口位置 >> 调度出的窗口位置
|
||||
// ★:[0] 1 2 3 4 >> 1
|
||||
// ★:0 1 [2] 3 4 >> 1
|
||||
// ★:0 1 2 [3] 4 >> 2
|
||||
// ★:0 1 2 3 [4] >> 3
|
||||
// ★:[0] >> 直接关闭当前窗口
|
||||
//LogUtils.d(TAG, "finish no yet.");
|
||||
IWinBoLLActivity preIWinBoLL = getPreIWinBoLL(iWinBoLL);
|
||||
iWinBoLL.getActivity().finish();
|
||||
if (preIWinBoLL != null) {
|
||||
resumeActivity(mGlobalApplication, preIWinBoLL);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 获取窗口队列中的前一个窗口
|
||||
//
|
||||
IWinBoLLActivity getPreIWinBoLL(IWinBoLLActivity iWinBoLL) {
|
||||
try {
|
||||
boolean bingo = false;
|
||||
IWinBoLLActivity preIWinBoLL = null;
|
||||
for (Map.Entry<String, IWinBoLLActivity> entity : _mapIWinBoLLList.entrySet()) {
|
||||
if (entity.getKey().equals(iWinBoLL.getTag())) {
|
||||
bingo = true;
|
||||
//LogUtils.d(TAG, "bingo");
|
||||
break;
|
||||
}
|
||||
preIWinBoLL = entity.getValue();
|
||||
}
|
||||
|
||||
if (bingo) {
|
||||
return preIWinBoLL;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// 从管理列表中移除管理项
|
||||
//
|
||||
public <T extends IWinBoLLActivity> boolean registeRemove(T activity) {
|
||||
IWinBoLLActivity iWinBoLLTest = _mapIWinBoLLList.get(activity.getTag());
|
||||
if (iWinBoLLTest != null) {
|
||||
_mapIWinBoLLList.remove(activity.getTag());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// 打印管理列表项列表里的信息
|
||||
//
|
||||
public static void printIWinBoLLListInfo() {
|
||||
//LogUtils.d(TAG, "printAvtivityListInfo");
|
||||
if (!_mapIWinBoLLList.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder("Map entries : " + Integer.toString(_mapIWinBoLLList.size()));
|
||||
Iterator<Map.Entry<String, IWinBoLLActivity>> iterator = _mapIWinBoLLList.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, IWinBoLLActivity> entry = iterator.next();
|
||||
sb.append("\nKey: " + entry.getKey() + ", \nValue: " + entry.getValue().getTag());
|
||||
//ToastUtils.show("\nKey: " + entry.getKey() + ", Value: " + entry.getValue().getTag());
|
||||
}
|
||||
sb.append("\nMap entries end.");
|
||||
LogUtils.d(TAG, sb.toString());
|
||||
} else {
|
||||
LogUtils.d(TAG, "The map is empty.");
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,12 +24,12 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "cc.winboll.studio.timestamp"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 30
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
// versionName 更新后需要手动设置
|
||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "15.0"
|
||||
versionName "15.1"
|
||||
if(true) {
|
||||
versionName = genVersionName("${versionName}")
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Tue May 06 18:33:32 HKT 2025
|
||||
stageCount=5
|
||||
#Wed May 07 15:07:02 HKT 2025
|
||||
stageCount=2
|
||||
libraryProject=
|
||||
baseVersion=15.0
|
||||
publishVersion=15.0.4
|
||||
baseVersion=15.1
|
||||
publishVersion=15.1.1
|
||||
buildCount=0
|
||||
baseBetaVersion=15.0.5
|
||||
baseBetaVersion=15.1.2
|
||||
|
@@ -2,7 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" >
|
||||
|
||||
<application>
|
||||
<application
|
||||
tools:replace="android:icon,android:roundIcon"
|
||||
android:icon="@drawable/ic_launcher_beta"
|
||||
android:roundIcon="@drawable/ic_launcher_beta">
|
||||
|
||||
<!-- Put flavor specific code here -->
|
||||
|
||||
|
@@ -14,8 +14,8 @@
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:icon="@drawable/ic_launcher_stage"
|
||||
android:roundIcon="@drawable/ic_launcher_stage"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/MyAppTheme"
|
||||
android:resizeableActivity="true"
|
||||
|
@@ -60,6 +60,10 @@ public class App extends GlobalApplication {
|
||||
//CrashHandler.getInstance().registerGlobal(this);
|
||||
//CrashHandler.getInstance().registerPart(this);
|
||||
}
|
||||
|
||||
public static int getAPPIcon() {
|
||||
return BuildConfig.DEBUG?R.drawable.ic_launcher_beta:R.drawable.ic_launcher_stage;
|
||||
}
|
||||
|
||||
public static void write(InputStream input, OutputStream output) throws IOException {
|
||||
byte[] buf = new byte[1024 * 8];
|
||||
|
@@ -26,11 +26,7 @@ import cc.winboll.studio.timestamp.receivers.ButtonClickReceiver;
|
||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
|
||||
import cc.winboll.studio.timestamp.utils.NotificationHelper;
|
||||
import cc.winboll.studio.timestamp.utils.ServiceUtil;
|
||||
import cc.winboll.studio.timestamp.utils.TimeStampRemoteViewsUtil;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import cc.winboll.studio.timestamp.utils.TimeStampUtil;
|
||||
|
||||
public class MainService extends Service {
|
||||
|
||||
@@ -59,10 +55,9 @@ public class MainService extends Service {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// 创建 RemoteViews 对象,并使用包含自定义 View 的布局
|
||||
//mRemoteViews = new RemoteViews(getPackageName(), R.layout.remoteviews_timestamp);
|
||||
|
||||
|
||||
mNotificationHelper = new NotificationHelper();
|
||||
mNotificationHelper.createServiceNotificationChannel(this);
|
||||
|
||||
// 创建广播接收器实例
|
||||
mButtonClickReceiver = new ButtonClickReceiver();
|
||||
|
||||
@@ -213,15 +208,10 @@ public class MainService extends Service {
|
||||
switch (message.what) {
|
||||
case MSG_UPDATE_TIMESTAMP:
|
||||
{
|
||||
long currentMillis = System.currentTimeMillis();
|
||||
Instant instant = Instant.ofEpochMilli(currentMillis);
|
||||
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
String szTimeStampFormatString = AppConfigsUtil.getInstance(MainService.this).getAppConfigsModel().getTimeStampFormatString();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(szTimeStampFormatString);
|
||||
String formattedDateTime = ldt.format(formatter);
|
||||
TimeStampRemoteViewsUtil.getInstance(MainService.this).showNotification(formattedDateTime + " 已复制");
|
||||
String szTimeStampShowString = TimeStampUtil.getInstance(MainService.this).getTimeStampShowString();
|
||||
mNotificationHelper.sendForegroundNotification(MainService.this, "时间戳:\n" + szTimeStampShowString + "\n已截取。");
|
||||
|
||||
LogUtils.d(TAG, "Hello, World! " + formattedDateTime);
|
||||
LogUtils.d(TAG, "Hello, World! " + szTimeStampShowString);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@@ -25,8 +25,8 @@ public class AppConfigsModel extends BaseBean {
|
||||
|
||||
public AppConfigsModel() {
|
||||
this.isEnableService = false;
|
||||
this.timeStampFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
this.timeStampCopyFormatString = "yyyy_MM_dd-HH_mm_ss";
|
||||
this.timeStampFormatString = "yyyy-MM-dd HH:mm:ss SSS";
|
||||
this.timeStampCopyFormatString = "yyyy_MM_dd-HH_mm_ss-SSS";
|
||||
}
|
||||
|
||||
public void setTimeStampCopyFormatString(String timeStampCopyFormatString) {
|
||||
|
@@ -10,36 +10,27 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.timestamp.BuildConfig;
|
||||
import cc.winboll.studio.timestamp.MainService;
|
||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
|
||||
import cc.winboll.studio.timestamp.utils.ClipboardUtil;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import cc.winboll.studio.timestamp.utils.TimeStampUtil;
|
||||
|
||||
public class ButtonClickReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final String TAG = "ButtonClickReceiver";
|
||||
|
||||
public static final String BUTTON_COPYTIMESTAMP_ACTION = "cc.winboll.studio.timestamp.receivers.ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION";
|
||||
public static final String BUTTON_COPYTIMESTAMP_ACTION = ButtonClickReceiver.class.getName() + (BuildConfig.DEBUG ?".DEBUG_": ".") + "BUTTON_COPYTIMESTAMP_ACTION";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
LogUtils.d(TAG, "onReceive");
|
||||
if (intent.getAction().equals(BUTTON_COPYTIMESTAMP_ACTION)) {
|
||||
// 在这里编写按钮点击后要执行的代码
|
||||
long currentMillis = System.currentTimeMillis();
|
||||
Instant instant = Instant.ofEpochMilli(currentMillis);
|
||||
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
String szTimeStampFormatString = AppConfigsUtil.getInstance(context).getAppConfigsModel().getTimeStampCopyFormatString();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(szTimeStampFormatString);
|
||||
String formattedDateTime = ldt.format(formatter);
|
||||
|
||||
ClipboardUtil.copyTextToClipboard(context, formattedDateTime);
|
||||
TimeStampUtil.getInstance(context).genTimeStamp();
|
||||
ClipboardUtil.copyTextToClipboard(context, TimeStampUtil.getInstance(context).getTimeStampCopyString());
|
||||
|
||||
// 比如显示一个Toast
|
||||
Toast.makeText(context, formattedDateTime + " 已复制", Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(context, "时间戳:\n" + TimeStampUtil.getInstance(context).getTimeStampCopyString() + "\n已拷贝到剪贴板。", Toast.LENGTH_SHORT).show();
|
||||
MainService.updateCopiedTimeStamp();
|
||||
}
|
||||
}
|
||||
|
@@ -22,12 +22,12 @@ public class AppConfigsUtil {
|
||||
|
||||
AppConfigsUtil(Context context) {
|
||||
this.mContext = context;
|
||||
loadAppConfigs();
|
||||
}
|
||||
|
||||
public synchronized static AppConfigsUtil getInstance(Context context){
|
||||
if(_AppConfigsUtil == null) {
|
||||
_AppConfigsUtil = new AppConfigsUtil(context);
|
||||
_AppConfigsUtil.loadAppConfigs();
|
||||
}
|
||||
return _AppConfigsUtil;
|
||||
}
|
||||
@@ -42,8 +42,8 @@ public class AppConfigsUtil {
|
||||
if (appConfigsModel != null) {
|
||||
mAppConfigsModel = appConfigsModel;
|
||||
} else {
|
||||
saveAppConfigs(new AppConfigsModel());
|
||||
_AppConfigsUtil = this;
|
||||
mAppConfigsModel = new AppConfigsModel();
|
||||
saveAppConfigs(mAppConfigsModel);
|
||||
}
|
||||
return mAppConfigsModel;
|
||||
}
|
||||
|
@@ -0,0 +1,71 @@
|
||||
package cc.winboll.studio.timestamp.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen
|
||||
* @Date 2025/05/07 02:38
|
||||
* @Describe AudioPlayer
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
public class AudioPlayerMP3Util {
|
||||
|
||||
public static final String TAG = "AudioPlayer";
|
||||
|
||||
private static MediaPlayer mediaPlayer;
|
||||
|
||||
/**
|
||||
* 播放指定的 MP3 文件
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param mp3FilePath MP3 文件的路径,例如:"/storage/emulated/0/Music/song.mp3"
|
||||
*/
|
||||
public static void playMp3(Context context, String mp3FilePath) {
|
||||
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.stop();
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
}
|
||||
|
||||
try {
|
||||
mediaPlayer = new MediaPlayer();
|
||||
Uri uri = Uri.parse(mp3FilePath);
|
||||
mediaPlayer.setDataSource(context, uri);
|
||||
mediaPlayer.prepare();
|
||||
mediaPlayer.start();
|
||||
|
||||
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
releaseMediaPlayer();
|
||||
}
|
||||
});
|
||||
|
||||
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
Log.e("AudioPlayer", "播放音频时出错: what=" + what + ", extra=" + extra);
|
||||
releaseMediaPlayer();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
releaseMediaPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放 MediaPlayer 资源
|
||||
*/
|
||||
private static void releaseMediaPlayer() {
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.stop();
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,69 @@
|
||||
package cc.winboll.studio.timestamp.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen
|
||||
* @Date 2025/05/07 02:31
|
||||
* @Describe AudioPlayerUtil
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
public class AudioPlayerUriUtil {
|
||||
|
||||
public static final String TAG = "AudioPlayerUtil";
|
||||
|
||||
private static MediaPlayer mediaPlayer;
|
||||
|
||||
/**
|
||||
* 播放指定Uri的音频
|
||||
* @param context 上下文
|
||||
* @param audioUri 音频的Uri
|
||||
*/
|
||||
public static void playAudio(Context context, Uri audioUri) {
|
||||
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.stop();
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
}
|
||||
|
||||
try {
|
||||
mediaPlayer = new MediaPlayer();
|
||||
mediaPlayer.setDataSource(context, audioUri);
|
||||
mediaPlayer.prepare();
|
||||
mediaPlayer.start();
|
||||
|
||||
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
releaseMediaPlayer();
|
||||
}
|
||||
});
|
||||
|
||||
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
Log.e("AudioPlayer", "播放音频时出错: what=" + what + ", extra=" + extra);
|
||||
releaseMediaPlayer();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
releaseMediaPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放MediaPlayer资源
|
||||
*/
|
||||
private static void releaseMediaPlayer() {
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.stop();
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,212 +12,195 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.graphics.Color;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.widget.RemoteViews;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.timestamp.App;
|
||||
import cc.winboll.studio.timestamp.MainActivity;
|
||||
import cc.winboll.studio.timestamp.MainService;
|
||||
import cc.winboll.studio.timestamp.R;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import cc.winboll.studio.timestamp.receivers.ButtonClickReceiver;
|
||||
|
||||
public class NotificationHelper {
|
||||
public static final String TAG = "NotificationHelper";
|
||||
|
||||
// 渠道ID和名称
|
||||
private static final String CHANNEL_ID_FOREGROUND = "foreground_channel";
|
||||
private static final String CHANNEL_NAME_FOREGROUND = "Foreground Service";
|
||||
private static final String CHANNEL_ID_TEMPORARY = "temporary_channel";
|
||||
private static final String CHANNEL_NAME_TEMPORARY = "Temporary Notifications";
|
||||
public static final String TAG = "NotificationUtil";
|
||||
public static final int ID_MSG_SERVICE = 10000;
|
||||
|
||||
// 通知ID
|
||||
public static final int FOREGROUND_NOTIFICATION_ID = 1001;
|
||||
public static final int TEMPORARY_NOTIFICATION_ID = 2001;
|
||||
static final String szSMSChannelID = "1";
|
||||
|
||||
private final Context mContext;
|
||||
private final NotificationManager mNotificationManager;
|
||||
static final String szServiceChannelID = "0";
|
||||
|
||||
// 示例:维护当前使用的渠道ID列表
|
||||
// 键:渠道ID,值:渠道重要性级别
|
||||
Map<String, Integer> activeChannelConfigs = new HashMap<>();
|
||||
|
||||
public NotificationHelper(Context context) {
|
||||
//static int mNumSendForegroundNotification = 10000;
|
||||
//static int mNumSendSMSNotification = 20000;
|
||||
Context mContext;
|
||||
|
||||
|
||||
public NotificationManager createServiceNotificationChannel(Context context) {
|
||||
mContext = context;
|
||||
mNotificationManager = context.getSystemService(NotificationManager.class);
|
||||
|
||||
// 初始化配置
|
||||
activeChannelConfigs.put(
|
||||
CHANNEL_ID_FOREGROUND,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
activeChannelConfigs.put(
|
||||
CHANNEL_ID_TEMPORARY,
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
|
||||
createNotificationChannels();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createForegroundChannel();
|
||||
createTemporaryChannel();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createForegroundChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_FOREGROUND,
|
||||
CHANNEL_NAME_FOREGROUND,
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
channel.setDescription("Persistent service notifications");
|
||||
//创建通知渠道ID
|
||||
String channelId = szServiceChannelID;
|
||||
//创建通知渠道名称
|
||||
String channelName = "Service Message";
|
||||
//创建通知渠道重要性
|
||||
int importance = NotificationManager.IMPORTANCE_MIN;
|
||||
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(false);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
return notificationManager;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void createTemporaryChannel() {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID_TEMPORARY,
|
||||
CHANNEL_NAME_TEMPORARY,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
);
|
||||
channel.setDescription("Temporary alert notifications");
|
||||
channel.setSound(null, null);
|
||||
channel.enableVibration(true);
|
||||
channel.setVibrationPattern(new long[]{100, 200, 300, 400});
|
||||
channel.setBypassDnd(true);
|
||||
mNotificationManager.createNotificationChannel(channel);
|
||||
public NotificationManager createSMSNotificationChannel(Context context) {
|
||||
//创建通知渠道ID
|
||||
String channelId = szSMSChannelID;
|
||||
//创建通知渠道名称
|
||||
String channelName = "SMS Message";
|
||||
//创建通知渠道重要性
|
||||
int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
|
||||
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), Notification.AUDIO_ATTRIBUTES_DEFAULT);
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
return notificationManager;
|
||||
}
|
||||
|
||||
// 显示常驻通知(通常用于前台服务)
|
||||
public Notification showForegroundNotification(Intent intent, String title, String content) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
// 创建通知
|
||||
//
|
||||
public void sendForegroundNotification(MainService service, String message) {
|
||||
//创建Notification,传入Context和channelId
|
||||
Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
|
||||
intent.setClass(mContext, MainActivity.class);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_FOREGROUND)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
//.setContentTitle(title)
|
||||
.setContentTitle(content)
|
||||
//.setContentText(content)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true)
|
||||
.build();
|
||||
//这里放一个count用来区分每一个通知
|
||||
//intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||
|
||||
mNotificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification);
|
||||
return notification;
|
||||
}
|
||||
|
||||
//参数1:context 上下文对象
|
||||
//参数2:发送者私有的请求码(Private request code for the sender)
|
||||
//参数3:intent 意图对象
|
||||
//参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
|
||||
PendingIntent mForegroundPendingIntent = PendingIntent.getActivity(service, ID_MSG_SERVICE, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
// 显示常驻通知(通常用于前台服务)
|
||||
public Notification showCustomForegroundNotification(Intent intent, RemoteViews contentView, RemoteViews bigContentView) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_FOREGROUND)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
//.setContentTitle(title)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContent(contentView)
|
||||
.setCustomBigContentView(bigContentView)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
Notification mForegroundNotification = new Notification.Builder(service, szServiceChannelID)
|
||||
.setAutoCancel(true)
|
||||
.setOngoing(true)
|
||||
//.setContentTitle(nessageNotificationBean.getTitle())
|
||||
//.setContentText(nessageNotificationBean.getContent())
|
||||
//.setContent(remoteviews)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(App.getAPPIcon())
|
||||
//设置红色
|
||||
.setColor(Color.parseColor("#F00606"))
|
||||
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), App.getAPPIcon()))
|
||||
.setContentIntent(mForegroundPendingIntent)
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification);
|
||||
return notification;
|
||||
}
|
||||
// 创建 RemoteViews 对象,加载布局
|
||||
RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification_layout);
|
||||
|
||||
// 显示临时通知(自动消失)
|
||||
public void showTemporaryNotification(Intent intent, String title, String content) {
|
||||
showTemporaryNotification(intent, TEMPORARY_NOTIFICATION_ID, title, content);
|
||||
}
|
||||
// 自定义 TextView 的文本
|
||||
remoteViews.setTextViewText(R.id.tv_timestamp, message);
|
||||
// 自定义 TextView 的文本颜色
|
||||
remoteViews.setTextColor(R.id.tv_timestamp, mContext.getResources().getColor(R.color.colorAccent, null));
|
||||
// 这里虽然不能直接设置字体大小,但可以通过反射等方式尝试(不推荐,且有兼容性问题)
|
||||
|
||||
// 显示临时通知(自动消失)
|
||||
public void showTemporaryNotification(Intent intent, int notificationID, String title, String content) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
// 设置按钮图片
|
||||
remoteViews.setImageViewResource(R.id.iv_copytimestamp, App.getAPPIcon());
|
||||
// 创建点击通知后的意图
|
||||
Intent intentMain = new Intent(mContext, MainActivity.class);
|
||||
PendingIntent pendingMainIntent = PendingIntent.getActivity(mContext, 0, intentMain, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// 设置通知的点击事件
|
||||
remoteViews.setOnClickPendingIntent(R.id.tv_timestamp, pendingMainIntent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.setVibrate(new long[]{100, 200, 300, 400})
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(notificationID, notification);
|
||||
}
|
||||
|
||||
// 创建自定义布局通知(可扩展)
|
||||
public void showCustomNotification(Intent intent, RemoteViews contentView, RemoteViews bigContentView) {
|
||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContent(contentView)
|
||||
.setCustomBigContentView(bigContentView)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.build();
|
||||
|
||||
mNotificationManager.notify(TEMPORARY_NOTIFICATION_ID + 1, notification);
|
||||
}
|
||||
|
||||
// 取消所有通知
|
||||
public void cancelAllNotifications() {
|
||||
mNotificationManager.cancelAll();
|
||||
}
|
||||
|
||||
// 取消指定通知
|
||||
public void cancelNotification(int notificationID) {
|
||||
mNotificationManager.cancel(notificationID);
|
||||
}
|
||||
|
||||
// 创建PendingIntent(兼容不同API版本)
|
||||
private PendingIntent createPendingIntent(Intent intent) {
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// flags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
// }
|
||||
return PendingIntent.getActivity(
|
||||
// 创建点击按钮后要发送的广播 Intent
|
||||
Intent broadcastIntent = new Intent(ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION);
|
||||
android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(
|
||||
mContext,
|
||||
0,
|
||||
intent,
|
||||
flags
|
||||
broadcastIntent,
|
||||
android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
}
|
||||
|
||||
// public void sendSMSReceivedMessage(int notificationID, String szPhone, String szBody) {
|
||||
// Intent intent = new Intent(mContext, SMSActivity.class);
|
||||
// intent.putExtra(SMSActivity.EXTRA_PHONE, szPhone);
|
||||
// String szTitle = mContext.getString(R.string.text_smsfrom) + "<" + szPhone + ">";
|
||||
// String szContent = "[ " + szBody + " ]";
|
||||
// showTemporaryNotification(intent, notificationID, szTitle, szContent);
|
||||
// 为按钮设置点击事件
|
||||
remoteViews.setOnClickPendingIntent(R.id.iv_copytimestamp, pendingIntent);
|
||||
|
||||
mForegroundNotification.contentView = remoteViews;
|
||||
mForegroundNotification.bigContentView = remoteViews;
|
||||
|
||||
service.startForeground(ID_MSG_SERVICE, mForegroundNotification);
|
||||
|
||||
// 播放默认短信铃声
|
||||
Uri defaultSmsRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
AudioPlayerUriUtil.playAudio(service, defaultSmsRingtoneUri);
|
||||
|
||||
// 播放应用铃声
|
||||
// 获取MP3文件的Uri
|
||||
Uri soundUri = Uri.parse("android.resource://" + service.getPackageName() + "/" + R.raw.diweiyi);
|
||||
AudioPlayerUriUtil.playAudio(service, soundUri);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public void sendSMSNotification(Context context, MessageNotificationBean messageNotificationBean) {
|
||||
// NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||
// Context.NOTIFICATION_SERVICE);
|
||||
// /*NotificationManager notificationManager = createSMSNotificationChannel(context);
|
||||
// if (notificationManager == null) {
|
||||
// LogUtils.d(TAG, "createSMSNotificationChannel failed.");
|
||||
// return;
|
||||
// }*/
|
||||
//
|
||||
// //创建Notification,传入Context和channelId
|
||||
// Intent intent = new Intent(context, SMSActivity.class);
|
||||
// intent.putExtra(SMSActivity.EXTRA_PHONE, messageNotificationBean.getPhone());
|
||||
// LogUtils.d(TAG, "sendSMSNotification(...) message.getPhone() is : " + messageNotificationBean.getPhone());
|
||||
// //Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
|
||||
// //intent.setClass(context, MainActivity.class);
|
||||
// //这里放一个count用来区分每一个通知
|
||||
// //intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||
//
|
||||
// //参数1:context 上下文对象
|
||||
// //参数2:发送者私有的请求码(Private request code for the sender)
|
||||
// //参数3:intent 意图对象
|
||||
// //参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
|
||||
// PendingIntent mRemindPendingIntent = PendingIntent.getActivity(context, messageNotificationBean.getMessageId(), intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
// Notification mSMSNotification = new Notification.Builder(context, szSMSChannelID)
|
||||
// .setAutoCancel(true)
|
||||
// .setContentTitle(messageNotificationBean.getTitle())
|
||||
// .setContentText(messageNotificationBean.getContent())
|
||||
// .setWhen(System.currentTimeMillis())
|
||||
// .setSmallIcon(R.drawable.ic_launcher)
|
||||
// //设置红色
|
||||
// .setColor(Color.parseColor("#F00606"))
|
||||
// .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher))
|
||||
// .setContentIntent(mRemindPendingIntent)
|
||||
// .build();
|
||||
//
|
||||
// RemoteViews mrvSMSNotificationView = new RemoteViews(context.getPackageName(), R.layout.remoteview);
|
||||
// mrvSMSNotificationView.setTextViewText(R.id.remoteviewTextView1, messageNotificationBean.getTitle());
|
||||
// mrvSMSNotificationView.setTextViewText(R.id.remoteviewTextView2, messageNotificationBean.getContent());
|
||||
// mrvSMSNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||
// mSMSNotification.contentView = mrvSMSNotificationView;
|
||||
// mSMSNotification.bigContentView = mrvSMSNotificationView;
|
||||
// notificationManager.notify(messageNotificationBean.getMessageId(), mSMSNotification);
|
||||
// LogUtils.d(TAG, "getMessageId is : " + Integer.toString(messageNotificationBean.getMessageId()));
|
||||
//
|
||||
// }
|
||||
|
||||
public void cleanOldChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
List<NotificationChannel> allChannels = mNotificationManager.getNotificationChannels();
|
||||
for (NotificationChannel channel : allChannels) {
|
||||
LogUtils.d(TAG, "Clean channel : " + channel.getId());
|
||||
if (!activeChannelConfigs.containsKey(channel.getId())) {
|
||||
// 安全删除渠道
|
||||
mNotificationManager.deleteNotificationChannel(channel.getId());
|
||||
LogUtils.d(TAG, String.format("Deleted Channel %s", channel.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// public void sendSMSReceivedMessage(Context context, int nMessageId, String szPhone, String szBody) {
|
||||
// String szTitle = context.getString(R.string.text_smsfrom) + "<" + szPhone + ">";
|
||||
// String szContent = "[ " + szBody + " ]";
|
||||
// sendSMSNotification(context, new MessageNotificationBean(nMessageId, szPhone, szTitle, szContent));
|
||||
// }
|
||||
|
||||
// public static void cancelNotification(Context context, int notificationId) {
|
||||
// // 获取 NotificationManager 实例
|
||||
// NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
// // 撤回指定 ID 的通知栏消息
|
||||
// notificationManager.cancel(notificationId);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,132 +0,0 @@
|
||||
package cc.winboll.studio.timestamp.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen
|
||||
* @Date 2025/05/05 21:10
|
||||
* @Describe TimeStampRemoteViewsUtil
|
||||
*/
|
||||
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.RemoteViews;
|
||||
import cc.winboll.studio.timestamp.MainActivity;
|
||||
import cc.winboll.studio.timestamp.R;
|
||||
import cc.winboll.studio.timestamp.receivers.ButtonClickReceiver;
|
||||
|
||||
public class TimeStampRemoteViewsUtil {
|
||||
|
||||
public static final String TAG = "TimeStampRemoteViewsUtil";
|
||||
|
||||
public static final String CHANNEL_ID = "TimeStampChannel";
|
||||
|
||||
static volatile TimeStampRemoteViewsUtil _TimeStampRemoteViewsUtil;
|
||||
Context mContext;
|
||||
NotificationHelper mNotificationHelper;
|
||||
RemoteViews mRemoteViews;
|
||||
Intent mIntentMain;
|
||||
|
||||
|
||||
|
||||
TimeStampRemoteViewsUtil(Context context) {
|
||||
mContext = context;
|
||||
mNotificationHelper = new NotificationHelper(context);
|
||||
//createNotificationChannel();
|
||||
}
|
||||
|
||||
public static synchronized TimeStampRemoteViewsUtil getInstance(Context context) {
|
||||
if (_TimeStampRemoteViewsUtil == null) {
|
||||
_TimeStampRemoteViewsUtil = new TimeStampRemoteViewsUtil(context);
|
||||
}
|
||||
return _TimeStampRemoteViewsUtil;
|
||||
}
|
||||
|
||||
// private void createNotificationChannel() {
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// CharSequence name = "自定义视图通知通道";
|
||||
// String description = "用于展示自定义视图的通知通道";
|
||||
// int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||
// NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
|
||||
// channel.setDescription(description);
|
||||
// NotificationManager notificationManager = mContext.getSystemService(NotificationManager.class);
|
||||
// notificationManager.createNotificationChannel(channel);
|
||||
// }
|
||||
// }
|
||||
|
||||
// public void showNotification(String msg) {
|
||||
// if (mRemoteViews == null) {
|
||||
// // 创建 RemoteViews 对象,加载布局
|
||||
// mRemoteViews = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification_layout);
|
||||
//
|
||||
// }
|
||||
// // 自定义 TextView 的文本
|
||||
// mRemoteViews.setTextViewText(R.id.tv_timestamp, msg);
|
||||
// // 自定义 TextView 的文本颜色
|
||||
// mRemoteViews.setTextColor(R.id.tv_timestamp, mContext.getResources().getColor(R.color.colorAccent, null));
|
||||
// // 这里虽然不能直接设置字体大小,但可以通过反射等方式尝试(不推荐,且有兼容性问题)
|
||||
//
|
||||
// // 创建点击通知后的意图
|
||||
// Intent intent = new Intent(mContext, MainActivity.class);
|
||||
// PendingIntent pendingMainIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// // 设置通知的点击事件
|
||||
// mRemoteViews.setOnClickPendingIntent(R.id.tv_timestamp, pendingMainIntent);
|
||||
//
|
||||
// // 创建点击按钮后要发送的广播 Intent
|
||||
// Intent broadcastIntent = new Intent(ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION);
|
||||
// android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(
|
||||
// mContext,
|
||||
// 0,
|
||||
// broadcastIntent,
|
||||
// android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
// );
|
||||
//
|
||||
// // 为按钮设置点击事件
|
||||
// mRemoteViews.setOnClickPendingIntent(R.id.btn_copytimestamp, pendingIntent);
|
||||
//
|
||||
// // 构建通知
|
||||
// NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID)
|
||||
// .setSmallIcon(android.R.drawable.ic_dialog_info)
|
||||
// .setContent(mRemoteViews)
|
||||
// .setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
// .setOngoing(true)
|
||||
// .setAutoCancel(true);
|
||||
//
|
||||
// // 显示通知
|
||||
// NotificationManager notificationManager = mContext.getSystemService(NotificationManager.class);
|
||||
// mNotification = builder.build();
|
||||
// notificationManager.notify(1, mNotification);
|
||||
// }
|
||||
|
||||
public void showNotification(String msg) {
|
||||
if (mRemoteViews == null) {
|
||||
// 创建 RemoteViews 对象,加载布局
|
||||
mRemoteViews = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification_layout);
|
||||
}
|
||||
// 自定义 TextView 的文本
|
||||
mRemoteViews.setTextViewText(R.id.tv_timestamp, msg);
|
||||
// 自定义 TextView 的文本颜色
|
||||
mRemoteViews.setTextColor(R.id.tv_timestamp, mContext.getResources().getColor(R.color.colorAccent, null));
|
||||
// 这里虽然不能直接设置字体大小,但可以通过反射等方式尝试(不推荐,且有兼容性问题)
|
||||
|
||||
// 创建点击通知后的意图
|
||||
mIntentMain = new Intent(mContext, MainActivity.class);
|
||||
PendingIntent pendingMainIntent = PendingIntent.getActivity(mContext, 0, mIntentMain, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// 设置通知的点击事件
|
||||
mRemoteViews.setOnClickPendingIntent(R.id.tv_timestamp, pendingMainIntent);
|
||||
|
||||
// 创建点击按钮后要发送的广播 Intent
|
||||
Intent broadcastIntent = new Intent(ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION);
|
||||
android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(
|
||||
mContext,
|
||||
0,
|
||||
broadcastIntent,
|
||||
android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
|
||||
// 为按钮设置点击事件
|
||||
mRemoteViews.setOnClickPendingIntent(R.id.btn_copytimestamp, pendingIntent);
|
||||
|
||||
mNotificationHelper.showCustomForegroundNotification(mIntentMain, mRemoteViews, mRemoteViews);
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
package cc.winboll.studio.timestamp.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen
|
||||
* @Date 2025/05/07 11:03
|
||||
* @Describe TimeStampUtil
|
||||
*/
|
||||
import android.content.Context;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class TimeStampUtil {
|
||||
|
||||
public static final String TAG = "TimeStampUtil";
|
||||
|
||||
volatile static TimeStampUtil _TimeStampUtil;
|
||||
|
||||
Context mContext;
|
||||
long mTimeStamp;
|
||||
|
||||
TimeStampUtil(Context context) {
|
||||
mContext = context;
|
||||
mTimeStamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public synchronized static TimeStampUtil getInstance(Context context) {
|
||||
if (_TimeStampUtil == null) {
|
||||
_TimeStampUtil = new TimeStampUtil(context);
|
||||
}
|
||||
return _TimeStampUtil;
|
||||
}
|
||||
|
||||
public void genTimeStamp() {
|
||||
mTimeStamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String getTimeStampShowString() {
|
||||
long currentMillis = mTimeStamp;
|
||||
Instant instant = Instant.ofEpochMilli(currentMillis);
|
||||
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
String szTimeStampFormatString = AppConfigsUtil.getInstance(mContext).getAppConfigsModel().getTimeStampFormatString();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(szTimeStampFormatString);
|
||||
String formattedDateTime = ldt.format(formatter);
|
||||
return formattedDateTime;
|
||||
}
|
||||
|
||||
public String getTimeStampCopyString() {
|
||||
long currentMillis = mTimeStamp;
|
||||
Instant instant = Instant.ofEpochMilli(currentMillis);
|
||||
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
String szTimeStampFormatString = AppConfigsUtil.getInstance(mContext).getAppConfigsModel().getTimeStampCopyFormatString();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(szTimeStampFormatString);
|
||||
String formattedDateTime = ldt.format(formatter);
|
||||
return formattedDateTime;
|
||||
}
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
15
timestamp/src/main/res/drawable/ic_launcher_beta.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:left="1dp"
|
||||
android:top="1dp"
|
||||
android:right="1dp"
|
||||
android:bottom="1dp"
|
||||
android:drawable="@drawable/ic_launcher_background"/>
|
||||
<item
|
||||
android:left="1dp"
|
||||
android:top="1dp"
|
||||
android:right="1dp"
|
||||
android:bottom="1dp"
|
||||
android:drawable="@drawable/ic_timestamp"/>
|
||||
</layer-list>
|
10
timestamp/src/main/res/drawable/ic_launcher_stage.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!--<item android:drawable="@drawable/ic_launcher_background"/>-->
|
||||
<item
|
||||
android:left="1dp"
|
||||
android:top="1dp"
|
||||
android:right="1dp"
|
||||
android:bottom="1dp"
|
||||
android:drawable="@drawable/ic_timestamp"/>
|
||||
</layer-list>
|
BIN
timestamp/src/main/res/drawable/ic_timestamp.png
Normal file
After Width: | Height: | Size: 10 KiB |
@@ -28,21 +28,21 @@
|
||||
<Switch
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="启用通知栏计时器"
|
||||
android:text="启用时间戳常驻通知栏"
|
||||
android:id="@+id/activitymainSwitch1"
|
||||
android:onClick="onSetMainServiceStatus"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Show Format :"
|
||||
android:text="Show Format Preview:"
|
||||
android:paddingRight="10dp"/>
|
||||
|
||||
<TextView
|
||||
@@ -69,7 +69,7 @@
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ApplySave"
|
||||
android:text="ApplyShow"
|
||||
android:id="@+id/btn_saveformatstring"
|
||||
android:textAllCaps="false"
|
||||
android:onClick="onSaveFormatString"/>
|
||||
@@ -77,14 +77,14 @@
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Copy Format :"
|
||||
android:text="Copy Format Preview:"
|
||||
android:paddingRight="10dp"/>
|
||||
|
||||
<TextView
|
||||
|
@@ -13,14 +13,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Text"
|
||||
android:textColor="#000000"
|
||||
android:textSize="18sp"
|
||||
android:textSize="14sp"
|
||||
android:layout_weight="1.0"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="复制时间戳"
|
||||
android:id="@+id/btn_copytimestamp"/>
|
||||
<ImageView
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:id="@+id/iv_copytimestamp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 15 KiB |
BIN
timestamp/src/main/res/raw/diweiyi.mp3
Normal file
@@ -2,7 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" >
|
||||
|
||||
<application>
|
||||
<application
|
||||
tools:replace="android:icon,android:roundIcon"
|
||||
android:icon="@drawable/ic_launcher_stage"
|
||||
android:roundIcon="@drawable/ic_launcher_stage">
|
||||
|
||||
<!-- Put flavor specific code here -->
|
||||
|
||||
|