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.