diff --git a/timestamp/build.properties b/timestamp/build.properties
index 640d410..1c9d593 100644
--- a/timestamp/build.properties
+++ b/timestamp/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Sun May 04 14:02:22 GMT 2025
+#Mon May 05 02:43:50 GMT 2025
stageCount=0
libraryProject=
baseVersion=15.0
publishVersion=15.0.0
-buildCount=2
+buildCount=8
baseBetaVersion=15.0.1
diff --git a/timestamp/src/main/AndroidManifest.xml b/timestamp/src/main/AndroidManifest.xml
index 29c023d..bff1a04 100644
--- a/timestamp/src/main/AndroidManifest.xml
+++ b/timestamp/src/main/AndroidManifest.xml
@@ -3,6 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.timestamp">
+
+
+
+
+
+
+
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/AssistantService.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/AssistantService.java
new file mode 100644
index 0000000..26f5b7c
--- /dev/null
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/AssistantService.java
@@ -0,0 +1,112 @@
+package cc.winboll.studio.timestamp;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2025/05/05 09:49
+ * @Describe MainService 守护进程服务
+ */
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import cc.winboll.studio.timestamp.AssistantService;
+import cc.winboll.studio.timestamp.MainService;
+import cc.winboll.studio.timestamp.models.AppConfigs;
+import cc.winboll.studio.timestamp.utils.ServiceUtil;
+
+public class AssistantService extends Service {
+
+ public static final String TAG = "AssistantService";
+
+ //MyBinder mMyBinder;
+ MyServiceConnection mMyServiceConnection;
+ volatile boolean mIsThreadAlive;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ //return mMyBinder;
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ //LogUtils.d(TAG, "call onCreate()");
+ super.onCreate();
+
+ //mMyBinder = new MyBinder();
+ if (mMyServiceConnection == null) {
+ mMyServiceConnection = new MyServiceConnection();
+ }
+ // 设置运行参数
+ mIsThreadAlive = false;
+ run();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ //LogUtils.d(TAG, "call onStartCommand(...)");
+ run();
+ AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs();
+ return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
+ }
+
+ /*class MyBinder extends IMyAidlInterface.Stub {
+ @Override
+ public String getServiceName() {
+ return AssistantService.class.getSimpleName();
+ }
+ }*/
+
+ @Override
+ public void onDestroy() {
+ //LogUtils.d(TAG, "call onDestroy()");
+ mIsThreadAlive = false;
+ super.onDestroy();
+ }
+
+ // 运行服务内容
+ //
+ void run() {
+ //LogUtils.d(TAG, "call run()");
+ AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs();
+ if (appConfigs.isEnableService()) {
+ if (mIsThreadAlive == false) {
+ // 设置运行状态
+ mIsThreadAlive = true;
+ // 唤醒和绑定主进程
+ wakeupAndBindMain();
+ }
+ }
+ }
+
+ // 唤醒和绑定主进程
+ //
+ void wakeupAndBindMain() {
+ if (ServiceUtil.isServiceAlive(getApplicationContext(), MainService.class.getName()) == false) {
+ //LogUtils.d(TAG, "wakeupAndBindMain() Wakeup... ControlCenterService");
+ startForegroundService(new Intent(AssistantService.this, MainService.class));
+ }
+ //LogUtils.d(TAG, "wakeupAndBindMain() Bind... ControlCenterService");
+ bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
+ }
+
+ // 主进程与守护进程连接时需要用到此类
+ //
+ class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ //LogUtils.d(TAG, "call onServiceConnected(...)");
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ //LogUtils.d(TAG, "call onServiceDisconnected(...)");
+ AppConfigs appConfigs = AppConfigs.getInstance(AssistantService.this).loadAppConfigs();
+ if (appConfigs.isEnableService()) {
+ wakeupAndBindMain();
+ }
+ }
+ }
+}
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/MainActivity.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/MainActivity.java
index 0184c2f..b557a09 100644
--- a/timestamp/src/main/java/cc/winboll/studio/timestamp/MainActivity.java
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/MainActivity.java
@@ -1,14 +1,17 @@
package cc.winboll.studio.timestamp;
import android.os.Bundle;
+import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogView;
import com.hjq.toast.ToastUtils;
+import android.widget.Switch;
public class MainActivity extends AppCompatActivity {
LogView mLogView;
+ Switch mswEnableMainService;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -18,8 +21,10 @@ public class MainActivity extends AppCompatActivity {
Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
+ mswEnableMainService = findViewById(R.id.activitymainSwitch1);
+
mLogView = findViewById(R.id.logview);
-
+
ToastUtils.show("onCreate");
}
@@ -28,4 +33,8 @@ public class MainActivity extends AppCompatActivity {
super.onResume();
mLogView.start();
}
+
+ public void onSetMainServiceStatus(View view) {
+ MainService.setMainServiceStatus(this, mswEnableMainService.isChecked());
+ }
}
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/MainService.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/MainService.java
new file mode 100644
index 0000000..4fd05e6
--- /dev/null
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/MainService.java
@@ -0,0 +1,157 @@
+package cc.winboll.studio.timestamp;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2025/05/05 09:47
+ * @Describe 主要服务
+ */
+import android.app.Notification;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.timestamp.AssistantService;
+import cc.winboll.studio.timestamp.MainService;
+import cc.winboll.studio.timestamp.models.AppConfigs;
+import cc.winboll.studio.timestamp.utils.NotificationHelper;
+import cc.winboll.studio.timestamp.utils.ServiceUtil;
+
+public class MainService extends Service {
+
+ public static String TAG = "MainService";
+
+ Notification notification;
+ private static boolean _mIsServiceAlive;
+ public static final String EXTRA_APKFILEPATH = "EXTRA_APKFILEPATH";
+ final static int MSG_INSTALL_APK = 0;
+ //Handler mHandler;
+ MyServiceConnection mMyServiceConnection;
+ MainActivity mInstallCompletedFollowUpActivity;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtils.d(TAG, "onCreate()");
+ _mIsServiceAlive = false;
+ //mHandler = new MyHandler(MainService.this);
+ if (mMyServiceConnection == null) {
+ mMyServiceConnection = new MyServiceConnection();
+ }
+
+ run();
+ }
+
+ private void run() {
+ AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs();
+ if (appConfigs.isEnableService()) {
+ if (_mIsServiceAlive == false) {
+ // 设置运行状态
+ _mIsServiceAlive = true;
+
+
+ // 显示前台通知栏
+ NotificationHelper helper = new NotificationHelper(this);
+ Intent intent = new Intent(this, MainActivity.class);
+ notification = helper.showForegroundNotification(intent, getString(R.string.app_name), getString(R.string.text_aboutservernotification));
+ startForeground(NotificationHelper.FOREGROUND_NOTIFICATION_ID, notification);
+
+ // 唤醒守护进程
+ wakeupAndBindAssistant();
+
+ LogUtils.d(TAG, "running...");
+
+ } else {
+ LogUtils.d(TAG, "_mIsServiceAlive is " + Boolean.toString(_mIsServiceAlive));
+
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ _mIsServiceAlive = false;
+ LogUtils.d(TAG, "onDestroy()");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtils.d(TAG, "onStartCommand");
+
+ run();
+ AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs();
+
+ return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
+ }
+
+ public static void setMainServiceStatus(Context context, boolean isEnable) {
+ AppConfigs appConfigs = AppConfigs.getInstance(context).loadAppConfigs();
+ appConfigs.setIsEnableService(isEnable);
+ Intent intent = new Intent(context, MainService.class);
+ if (isEnable) {
+ context.startService(intent);
+ } else {
+ context.stopService(intent);
+ }
+ }
+
+ // 主进程与守护进程连接时需要用到此类
+ //
+ private class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ //LogUtils.d(TAG, "call onServiceConnected(...)");
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ //LogUtils.d(TAG, "call onServiceConnected(...)");
+ AppConfigs appConfigs = AppConfigs.getInstance(MainService.this).loadAppConfigs();
+ if (appConfigs.isEnableService()) {
+ // 唤醒守护进程
+ wakeupAndBindAssistant();
+ }
+ }
+ }
+
+ // 唤醒和绑定守护进程
+ //
+ void wakeupAndBindAssistant() {
+ if (ServiceUtil.isServiceAlive(getApplicationContext(), AssistantService.class.getName()) == false) {
+ startService(new Intent(MainService.this, AssistantService.class));
+ //LogUtils.d(TAG, "call wakeupAndBindAssistant() : Binding... AssistantService");
+ bindService(new Intent(MainService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
+ }
+ }
+
+ //
+ // 服务事务处理类
+ //
+// static class MyHandler extends Handler {
+// WeakReference weakReference;
+// MyHandler(MainService service) {
+// weakReference = new WeakReference(service);
+// }
+// public void handleMessage(Message message) {
+// MainService theActivity = weakReference.get();
+// switch (message.what) {
+// case MSG_INSTALL_APK:
+// {
+// break;
+// }
+// default:
+// break;
+// }
+// super.handleMessage(message);
+// }
+// }
+}
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/models/AppConfigs.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/models/AppConfigs.java
new file mode 100644
index 0000000..7457766
--- /dev/null
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/models/AppConfigs.java
@@ -0,0 +1,128 @@
+package cc.winboll.studio.timestamp.models;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2025/05/05 09:51
+ * @Describe 应用配置数据模型
+ */
+import android.content.Context;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.timestamp.models.AppConfigs;
+import cc.winboll.studio.timestamp.utils.FileUtil;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+public class AppConfigs implements Serializable {
+
+ public static final String TAG = "AppConfigs";
+
+ volatile static AppConfigs _AppConfigs;
+ Context mContext;
+
+ // 是否启动服务
+ boolean isEnableService;
+
+ AppConfigs(Context context) {
+ this.mContext = context;
+ this.isEnableService = false;
+ }
+
+ public synchronized static AppConfigs getInstance(Context context) {
+ if (_AppConfigs == null) {
+ _AppConfigs = new AppConfigs(context);
+ _AppConfigs.loadAppConfigs();
+ }
+ return _AppConfigs;
+ }
+
+ public void setIsEnableService(boolean isEnableService) {
+ this.isEnableService = isEnableService;
+ }
+
+ public boolean isEnableService() {
+ return isEnableService;
+ }
+
+ @Override
+ public String toString() {
+ // 创建 JsonWriter 对象
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new
+ JsonWriter(stringWriter);
+ try {
+ // 开始 JSON 对象
+ writer.beginObject();
+
+ // 写入键值对
+ writer.name("isEnableService").value(this.isEnableService);
+
+ // 结束 JSON 对象
+ writer.endObject();
+ return stringWriter.toString();
+ } catch (IOException e) {
+ LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
+ }
+ // 获取 JSON 字符串
+ return "";
+ }
+
+ public AppConfigs parseAppConfigs(String szAppConfigs) {
+ // 创建 JsonWriter 对象
+ StringReader stringReader = new StringReader(szAppConfigs);
+ JsonReader jsonReader = new
+ JsonReader(stringReader);
+ try {
+ // 开始 JSON 对象
+ jsonReader.beginObject();
+
+ // 写入键值对
+ while (jsonReader.hasNext()) {
+ String name = jsonReader.nextName();
+ if (name.equals("isEnableService")) {
+ setIsEnableService(jsonReader.nextBoolean());
+ } else {
+ jsonReader.skipValue();
+ }
+ }
+ // 结束 JSON 对象
+ jsonReader.endObject();
+ return this;
+ } catch (IOException e) {
+ LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
+ }
+ // 获取 JSON 字符串
+ return null;
+ }
+
+ static String getDataPath(Context context) {
+ return context.getExternalFilesDir(TAG) + "/" + TAG + ".json";
+ }
+
+ public AppConfigs loadAppConfigs() {
+ AppConfigs appConfigs = null;
+ try {
+ String szJson = FileUtil.readFile(getDataPath(mContext));
+ appConfigs = parseAppConfigs(szJson);
+
+ if (appConfigs != null) {
+ _AppConfigs = appConfigs;
+ }
+ } catch (IOException e) {
+ LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
+ }
+ return _AppConfigs;
+ }
+
+ public void saveAppConfigs(AppConfigs appConfigs) {
+ try {
+ String szJson = appConfigs.toString();
+ FileUtil.writeFile(getDataPath(mContext), szJson);
+ } catch (IOException e) {
+ LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
+ }
+ }
+}
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/FileUtil.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/FileUtil.java
new file mode 100644
index 0000000..883e443
--- /dev/null
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/FileUtil.java
@@ -0,0 +1,47 @@
+package cc.winboll.studio.timestamp.utils;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2025/05/05 09:52
+ * @Describe 文件管理工具类
+ */
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+
+public class FileUtil {
+
+ public static final String TAG = "FileUtil";
+
+ //
+ // 把字符串写入文件,指定 UTF-8 编码
+ //
+ public static void writeFile(String filePath, String content) throws IOException {
+ File file = new File(filePath);
+ FileOutputStream outputStream = new FileOutputStream(file);
+ OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
+ writer.write(content);
+ writer.close();
+ }
+
+ //
+ // 读取文件到字符串,指定 UTF-8 编码
+ //
+ public static String readFile(String filePath) throws IOException {
+ File file = new File(filePath);
+ FileInputStream inputStream = new FileInputStream(file);
+ InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
+ StringBuilder content = new StringBuilder();
+ int character;
+ while ((character = reader.read()) != -1) {
+ content.append((char) character);
+ }
+ reader.close();
+ return content.toString();
+ }
+
+}
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/NotificationHelper.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/NotificationHelper.java
new file mode 100644
index 0000000..0e9ef9a
--- /dev/null
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/NotificationHelper.java
@@ -0,0 +1,201 @@
+package cc.winboll.studio.timestamp.utils;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2025/05/05 10:36
+ * @Describe 应用通知工具类
+ */
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+import android.widget.RemoteViews;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.timestamp.R;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class NotificationHelper {
+ public static final String TAG = "NotificationHelper";
+
+ // 渠道ID和名称
+ private static final String CHANNEL_ID_FOREGROUND = "foreground_channel";
+ private static final String CHANNEL_NAME_FOREGROUND = "Foreground Service";
+ private static final String CHANNEL_ID_TEMPORARY = "temporary_channel";
+ private static final String CHANNEL_NAME_TEMPORARY = "Temporary Notifications";
+
+ // 通知ID
+ public static final int FOREGROUND_NOTIFICATION_ID = 1001;
+ public static final int TEMPORARY_NOTIFICATION_ID = 2001;
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+
+ // 示例:维护当前使用的渠道ID列表
+ // 键:渠道ID,值:渠道重要性级别
+ Map activeChannelConfigs = new HashMap<>();
+
+ public NotificationHelper(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");
+ channel.setSound(null, null);
+ channel.enableVibration(false);
+ mNotificationManager.createNotificationChannel(channel);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private void createTemporaryChannel() {
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID_TEMPORARY,
+ CHANNEL_NAME_TEMPORARY,
+ NotificationManager.IMPORTANCE_HIGH
+ );
+ channel.setDescription("Temporary alert notifications");
+ channel.setSound(null, null);
+ channel.enableVibration(true);
+ channel.setVibrationPattern(new long[]{100, 200, 300, 400});
+ channel.setBypassDnd(true);
+ mNotificationManager.createNotificationChannel(channel);
+ }
+
+ // 显示常驻通知(通常用于前台服务)
+ public Notification showForegroundNotification(Intent intent, String title, String content) {
+ PendingIntent pendingIntent = createPendingIntent(intent);
+
+ Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_FOREGROUND)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
+ //.setContentTitle(title)
+ .setContentTitle(content)
+ //.setContentText(content)
+ .setContentIntent(pendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setOngoing(true)
+ .build();
+
+ mNotificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification);
+ return notification;
+ }
+
+ // 显示临时通知(自动消失)
+ public void showTemporaryNotification(Intent intent, String title, String content) {
+ showTemporaryNotification(intent, TEMPORARY_NOTIFICATION_ID, title, content);
+ }
+
+ // 显示临时通知(自动消失)
+ public void showTemporaryNotification(Intent intent, int notificationID, String title, String content) {
+ PendingIntent pendingIntent = createPendingIntent(intent);
+
+ Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
+ .setContentTitle(title)
+ .setContentText(content)
+ .setContentIntent(pendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setAutoCancel(true)
+ .setVibrate(new long[]{100, 200, 300, 400})
+ .build();
+
+ mNotificationManager.notify(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(
+ mContext,
+ 0,
+ intent,
+ flags
+ );
+ }
+
+// 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);
+// }
+
+ public void cleanOldChannels() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ List 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()));
+ }
+ }
+ }
+ }
+}
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/NotificationUtil.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/NotificationUtil.java
new file mode 100644
index 0000000..ca71fbf
--- /dev/null
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/NotificationUtil.java
@@ -0,0 +1,90 @@
+package cc.winboll.studio.timestamp.utils;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2025/05/05 09:45
+ * @Describe 通知栏工具
+ */
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.media.RingtoneManager;
+import android.os.Build;
+import android.util.Log;
+import android.widget.RemoteViews;
+import cc.winboll.studio.timestamp.MainService;
+import cc.winboll.studio.timestamp.MainActivity;
+import cc.winboll.studio.timestamp.R;
+
+public class NotificationUtil {
+
+ public static final String TAG = "NotificationUtil";
+
+
+
+ static final String szServiceChannelID = "0";
+ static int mNumSendForegroundNotification = 10000;
+
+ public NotificationManager createServiceNotificationChannel(Context context) {
+ //创建通知渠道ID
+ String channelId = szServiceChannelID;
+ //创建通知渠道名称
+ String channelName = "Service Message";
+ //创建通知渠道重要性
+ int importance = NotificationManager.IMPORTANCE_MIN;
+ NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
+ channel.setSound(null, null);
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ notificationManager.createNotificationChannel(channel);
+ return notificationManager;
+ }
+
+ // 创建通知
+ //
+ public void sendForegroundNotification(MainService service) {
+ //创建Notification,传入Context和channelId
+ Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
+ intent.setClass(service, MainActivity.class);
+
+ //这里放一个count用来区分每一个通知
+ //intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
+
+ //参数1:context 上下文对象
+ //参数2:发送者私有的请求码(Private request code for the sender)
+ //参数3:intent 意图对象
+ //参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
+ PendingIntent mForegroundPendingIntent = PendingIntent.getActivity(service, mNumSendForegroundNotification, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
+
+ Notification mForegroundNotification = new Notification.Builder(service, szServiceChannelID)
+ .setAutoCancel(true)
+ .setContentTitle(service.getString(R.string.app_name))
+ .setContentText(service.TAG + " is started.")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.drawable.ic_launcher)
+ //设置红色
+ .setColor(Color.parseColor("#F00606"))
+ .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
+ .setContentIntent(mForegroundPendingIntent)
+ .build();
+
+
+ /*RemoteViews mrvForegroundNotificationView = new RemoteViews(service.getPackageName(), R.layout.remoteview);
+ mrvForegroundNotificationView.setTextViewText(R.id.remoteviewTextView1, notificationMessage.getTitle());
+ mrvForegroundNotificationView.setTextViewText(R.id.remoteviewTextView2, notificationMessage.getContent());
+ mrvForegroundNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
+ mForegroundNotification.contentView = mrvForegroundNotificationView;
+ mForegroundNotification.bigContentView = mrvForegroundNotificationView;
+ */
+
+ service.startForeground(mNumSendForegroundNotification, mForegroundNotification);
+
+ }
+}
+
+
diff --git a/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/ServiceUtil.java b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/ServiceUtil.java
new file mode 100644
index 0000000..113c239
--- /dev/null
+++ b/timestamp/src/main/java/cc/winboll/studio/timestamp/utils/ServiceUtil.java
@@ -0,0 +1,35 @@
+package cc.winboll.studio.timestamp.utils;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2025/05/05 09:58
+ * @Describe 应用服务管理类
+ */
+import android.app.ActivityManager;
+import android.content.Context;
+import java.util.List;
+
+public class ServiceUtil {
+
+ public final static String TAG = "ServiceUtil";
+
+ public static boolean isServiceAlive(Context context, String szServiceName) {
+ // 获取Activity管理者对象
+ ActivityManager manager = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ // 获取正在运行的服务(此处设置最多取1000个)
+ List runningServices = manager
+ .getRunningServices(1000);
+ if (runningServices.size() <= 0) {
+ return false;
+ }
+ // 遍历,若存在名字和传入的serviceName的一致则说明存在
+ for (ActivityManager.RunningServiceInfo runningServiceInfo : runningServices) {
+ if (runningServiceInfo.service.getClassName().equals(szServiceName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/timestamp/src/main/res/layout/activity_main.xml b/timestamp/src/main/res/layout/activity_main.xml
index 12fdc32..d60c8cb 100644
--- a/timestamp/src/main/res/layout/activity_main.xml
+++ b/timestamp/src/main/res/layout/activity_main.xml
@@ -19,6 +19,20 @@
+
+
+
+
+
+
-
-
TimeStamp
+ This is the prompt window when the SMS service runs, which you can set to hide this class notification in the notification message settings.