Merge remote-tracking branch 'origin/timestamp' into appbase
This commit is contained in:
		
							
								
								
									
										14
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -1,11 +1,8 @@ | ||||
| // 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" } | ||||
|          | ||||
|         //本地 maven 库 | ||||
|         maven { url "file:///sdcard/.aide/maven/" } | ||||
|         // Nexus Maven 库地址 | ||||
|         // "WinBoLL Release" | ||||
|         maven { url "https://nexus.winboll.cc/repository/maven-public/" } | ||||
| @@ -31,11 +28,8 @@ buildscript { | ||||
|  | ||||
| allprojects { | ||||
|     repositories { | ||||
|         // 本地 Maven 仓库(默认路径为 ~/.m2/repository) | ||||
|         //mavenLocal() | ||||
|         // 或自定义本地仓库路径 | ||||
|         maven { url "file:///sdcard/.m2/repository" } | ||||
|          | ||||
|         //本地 maven 库 | ||||
|         maven { url "file:///sdcard/.aide/maven/" } | ||||
|         // Nexus Maven 库地址 | ||||
|         // "WinBoLL Release" | ||||
|         maven { url "https://nexus.winboll.cc/repository/maven-public/" } | ||||
|   | ||||
							
								
								
									
										6
									
								
								timestamp/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								timestamp/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| ## TimpStamp | ||||
| ## 时间戳工具集 | ||||
|  | ||||
| ## 使用要点: | ||||
| 1。常驻通知栏按钮的正常使用, | ||||
|    需要设置允许应用[写入剪贴板]的[始终允许]权限。 | ||||
| @@ -24,7 +24,7 @@ android { | ||||
|     defaultConfig { | ||||
|         applicationId "cc.winboll.studio.timestamp" | ||||
|         minSdkVersion 24 | ||||
|         targetSdkVersion 30 | ||||
|         targetSdkVersion 29 | ||||
|         versionCode 1 | ||||
|         // versionName 更新后需要手动设置  | ||||
|         // .winboll/winbollBuildProps.properties 文件的 stageCount=0 | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #Created by .winboll/winboll_app_build.gradle | ||||
| #Tue May 06 11:17:44 HKT 2025 | ||||
| stageCount=1 | ||||
| #Wed May 07 13:33:12 HKT 2025 | ||||
| stageCount=12 | ||||
| libraryProject= | ||||
| baseVersion=15.0 | ||||
| publishVersion=15.0.0 | ||||
| publishVersion=15.0.11 | ||||
| buildCount=0 | ||||
| baseBetaVersion=15.0.1 | ||||
| baseBetaVersion=15.0.12 | ||||
|   | ||||
| @@ -5,13 +5,17 @@ | ||||
|  | ||||
|     <!-- 运行前台服务 --> | ||||
|     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> | ||||
|  | ||||
|     <!-- READ_CLIPBOARD --> | ||||
|     <uses-permission android:name="android.permission.READ_CLIPBOARD"/> | ||||
|  | ||||
|     <!-- WRITE_CLIPBOARD --> | ||||
|     <uses-permission android:name="android.permission.WRITE_CLIPBOARD"/> | ||||
|  | ||||
|     <application | ||||
|         android:allowBackup="true" | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
|         android:roundIcon="@mipmap/ic_launcher_round" | ||||
|         android:icon="@drawable/ic_launcher" | ||||
|         android:roundIcon="@drawable/ic_launcher" | ||||
|         android:label="@string/app_name" | ||||
|         android:theme="@style/MyAppTheme" | ||||
|         android:resizeableActivity="true" | ||||
|   | ||||
| @@ -6,6 +6,7 @@ package cc.winboll.studio.timestamp; | ||||
|  * @Describe 主要服务 | ||||
|  */ | ||||
| import android.app.Notification; | ||||
| import android.app.NotificationManager; | ||||
| import android.app.Service; | ||||
| import android.content.ComponentName; | ||||
| import android.content.Context; | ||||
| @@ -25,13 +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 java.util.Timer; | ||||
| import java.util.TimerTask; | ||||
| import cc.winboll.studio.timestamp.utils.TimeStampUtil; | ||||
|  | ||||
| public class MainService extends Service { | ||||
|  | ||||
| @@ -44,11 +39,11 @@ public class MainService extends Service { | ||||
|     Notification mNotification; | ||||
|     RemoteViews mRemoteViews; | ||||
|     TextView mtvTimeStamp; | ||||
|     Timer mTimer; | ||||
|     //Timer mTimer; | ||||
|     private static boolean _mIsServiceAlive; | ||||
|     public static final String EXTRA_APKFILEPATH = "EXTRA_APKFILEPATH"; | ||||
|     final static int MSG_INSTALL_APK = 0; | ||||
|     MyHandler mMyHandler; | ||||
|     static MyHandler _MyHandler; | ||||
|     MyServiceConnection mMyServiceConnection; | ||||
|     MainActivity mInstallCompletedFollowUpActivity; | ||||
|  | ||||
| @@ -60,9 +55,8 @@ 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(); | ||||
| @@ -76,7 +70,7 @@ public class MainService extends Service { | ||||
|         LogUtils.d(TAG, "onCreate()"); | ||||
|         _mIsServiceAlive = false; | ||||
|  | ||||
|         mMyHandler = new MyHandler(); | ||||
|         _MyHandler = new MyHandler(); | ||||
|         if (mMyServiceConnection == null) { | ||||
|             mMyServiceConnection = new MyServiceConnection(); | ||||
|         } | ||||
| @@ -101,17 +95,18 @@ public class MainService extends Service { | ||||
|                 wakeupAndBindAssistant(); | ||||
|  | ||||
|                 LogUtils.d(TAG, "running..."); | ||||
|                 _MyHandler.sendEmptyMessage(MSG_UPDATE_TIMESTAMP); | ||||
|  | ||||
|                 mTimer = new Timer(); | ||||
|                 TimerTask task = new TimerTask() { | ||||
|                     @Override | ||||
|                     public void run() { | ||||
|                         //System.out.println("定时任务执行了"); | ||||
|                         mMyHandler.sendEmptyMessage(MSG_UPDATE_TIMESTAMP); | ||||
|                     } | ||||
|                 }; | ||||
|                 // 延迟1秒后开始执行,之后每隔100毫秒执行一次 | ||||
|                 mTimer.schedule(task, 1000, 100); | ||||
| //                mTimer = new Timer(); | ||||
| //                TimerTask task = new TimerTask() { | ||||
| //                    @Override | ||||
| //                    public void run() { | ||||
| //                        //System.out.println("定时任务执行了"); | ||||
| //                        mMyHandler.sendEmptyMessage(MSG_UPDATE_TIMESTAMP); | ||||
| //                    } | ||||
| //                }; | ||||
| //                // 延迟1秒后开始执行,之后每隔100毫秒执行一次 | ||||
| //                mTimer.schedule(task, 1000, 100); | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -125,11 +120,17 @@ public class MainService extends Service { | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         if (mTimer != null) { | ||||
|             mTimer.cancel(); | ||||
|         } | ||||
|  | ||||
|  | ||||
| //        if (mTimer != null) { | ||||
| //            mTimer.cancel(); | ||||
| //        } | ||||
|  | ||||
|         NotificationManager notificationManager = getSystemService(NotificationManager.class); | ||||
|         notificationManager.cancelAll(); | ||||
|  | ||||
|         _mIsServiceAlive = false; | ||||
|         _MyHandler = null; | ||||
|         LogUtils.d(TAG, "onDestroy()"); | ||||
|     } | ||||
|  | ||||
| @@ -207,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"); | ||||
|                         LogUtils.d(TAG, "Hello, World! " + szTimeStampShowString); | ||||
|                         break; | ||||
|                     } | ||||
|                 default: | ||||
| @@ -224,4 +220,10 @@ public class MainService extends Service { | ||||
|             super.handleMessage(message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void updateCopiedTimeStamp() { | ||||
|         if (_MyHandler != null) { | ||||
|             _MyHandler.sendEmptyMessage(MSG_UPDATE_TIMESTAMP); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
| @@ -63,6 +63,7 @@ public class AppConfigsModel extends BaseBean { | ||||
|         super.writeThisToJsonWriter(jsonWriter); | ||||
|         jsonWriter.name("isEnableService").value(isEnableService()); | ||||
|         jsonWriter.name("timeStampFormatString").value(getTimeStampFormatString()); | ||||
|         jsonWriter.name("timeStampCopyFormatString").value(getTimeStampCopyFormatString()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -72,6 +73,8 @@ public class AppConfigsModel extends BaseBean { | ||||
|                 setIsEnableService(jsonReader.nextBoolean()); | ||||
|             } else if (name.equals("timeStampFormatString")) { | ||||
|                 setTimeStampFormatString(jsonReader.nextString()); | ||||
|             } else if (name.equals("timeStampCopyFormatString")) { | ||||
|                 setTimeStampCopyFormatString(jsonReader.nextString()); | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|   | ||||
| @@ -10,12 +10,9 @@ import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.widget.Toast; | ||||
| import cc.winboll.studio.libappbase.LogUtils; | ||||
| import cc.winboll.studio.timestamp.utils.AppConfigsUtil; | ||||
| import cc.winboll.studio.timestamp.MainService; | ||||
| 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 { | ||||
|  | ||||
| @@ -28,17 +25,12 @@ public class ButtonClickReceiver extends BroadcastReceiver { | ||||
|         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,190 @@ 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.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_TEMPORARY) | ||||
|             .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) | ||||
|             .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) | ||||
|             //.setContentTitle(nessageNotificationBean.getTitle()) | ||||
|             //.setContentText(nessageNotificationBean.getContent()) | ||||
|             //.setContent(remoteviews) | ||||
|             .setWhen(System.currentTimeMillis()) | ||||
|             .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}) | ||||
|             //设置红色 | ||||
|             .setColor(Color.parseColor("#F00606")) | ||||
|             .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher)) | ||||
|             .setContentIntent(mForegroundPendingIntent) | ||||
|             .build(); | ||||
|  | ||||
|         mNotificationManager.notify(notificationID, notification); | ||||
|     } | ||||
|         // 创建 RemoteViews 对象,加载布局 | ||||
|         RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification_layout); | ||||
|  | ||||
|     // 创建自定义布局通知(可扩展) | ||||
|     public void showCustomNotification(Intent intent, RemoteViews contentView, RemoteViews bigContentView) { | ||||
|         PendingIntent pendingIntent = createPendingIntent(intent); | ||||
|         // 自定义 TextView 的文本 | ||||
|         remoteViews.setTextViewText(R.id.tv_timestamp, message); | ||||
|         // 自定义 TextView 的文本颜色 | ||||
|         remoteViews.setTextColor(R.id.tv_timestamp, mContext.getResources().getColor(R.color.colorAccent, null)); | ||||
|         // 这里虽然不能直接设置字体大小,但可以通过反射等方式尝试(不推荐,且有兼容性问题) | ||||
|  | ||||
|         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(); | ||||
|         // 创建点击通知后的意图 | ||||
|         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); | ||||
|  | ||||
|         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 | ||||
|         ); | ||||
|  | ||||
|         // 为按钮设置点击事件 | ||||
|         remoteViews.setOnClickPendingIntent(R.id.btn_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 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 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,96 +0,0 @@ | ||||
| package cc.winboll.studio.timestamp.utils; | ||||
|  | ||||
| /** | ||||
|  * @Author ZhanGSKen | ||||
|  * @Date 2025/05/05 21:10 | ||||
|  * @Describe TimeStampRemoteViewsUtil | ||||
|  */ | ||||
| import android.app.NotificationChannel; | ||||
| import android.app.NotificationManager; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.os.Build; | ||||
| import android.widget.RemoteViews; | ||||
| import android.widget.TextView; | ||||
| import androidx.core.app.NotificationCompat; | ||||
| 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; | ||||
|     RemoteViews mRemoteViews; | ||||
|     TextView mtvMessage; | ||||
|  | ||||
|     TimeStampRemoteViewsUtil(Context context) { | ||||
|         mContext = 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 pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); | ||||
|         // 设置通知的点击事件 | ||||
|         //mRemoteViews.setOnClickPendingIntent(R.id.btn_copytimestamp, pendingIntent); | ||||
|          | ||||
|         // 创建点击按钮后要发送的广播 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); | ||||
|         notificationManager.notify(1, builder.build()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,57 @@ | ||||
| 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; | ||||
|     } | ||||
|  | ||||
|     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().getTimeStampCopyFormatString(); | ||||
|         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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								timestamp/src/main/res/drawable/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								timestamp/src/main/res/drawable/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 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,14 @@ | ||||
| 		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"/> | ||||
| 		android:layout_width="60dp" | ||||
| 		android:layout_height="60dp" | ||||
| 		android:id="@+id/btn_copytimestamp" | ||||
| 		android:background="@drawable/ic_launcher"/> | ||||
|  | ||||
| </LinearLayout> | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								timestamp/src/main/res/raw/diweiyi.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								timestamp/src/main/res/raw/diweiyi.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,5 +1,5 @@ | ||||
| <resources> | ||||
|     <string name="app_name">TimeStamp</string> | ||||
|     <string name="text_aboutservernotification">This is the prompt window when the SMS service runs, which you can set to hide this class notification in the notification message settings.</string> | ||||
|      | ||||
|     <string name="accessibility_service_description">Accessibility service description.</string> | ||||
| </resources> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen