Compare commits
26 Commits
timestamp-
...
timestamp-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7177f5fa5 | ||
|
|
9e8263ae8f | ||
|
|
5ee9da1982 | ||
|
|
f59f7676b7 | ||
|
|
19b8251182 | ||
|
|
4dadb15c0c | ||
|
|
21b5487513 | ||
|
|
f49c8141a8 | ||
|
|
6f3ce092ca | ||
|
|
29785be88d | ||
|
|
3f809e4c7a | ||
|
|
9c3c485ecd | ||
|
|
336511931d | ||
|
|
d3da3304ee | ||
|
|
a5295a0744 | ||
|
|
9b761b35ce | ||
|
|
5206f8781e | ||
|
|
2c0a5bfe65 | ||
|
|
49bce0f773 | ||
|
|
07c6778212 | ||
|
|
1cdad9ac7d | ||
|
|
986922c1fa | ||
|
|
bff40d2a64 | ||
|
|
13e0ad3f03 | ||
|
|
be85ef923e | ||
|
|
9a3383a43b |
14
build.gradle
@@ -1,11 +1,8 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
// 本地 Maven 仓库(默认路径为 ~/.m2/repository)
|
//本地 maven 库
|
||||||
//mavenLocal()
|
maven { url "file:///sdcard/.aide/maven/" }
|
||||||
// 或自定义本地仓库路径
|
|
||||||
maven { url "file:///sdcard/.m2/repository" }
|
|
||||||
|
|
||||||
// Nexus Maven 库地址
|
// Nexus Maven 库地址
|
||||||
// "WinBoLL Release"
|
// "WinBoLL Release"
|
||||||
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
||||||
@@ -31,11 +28,8 @@ buildscript {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
// 本地 Maven 仓库(默认路径为 ~/.m2/repository)
|
//本地 maven 库
|
||||||
//mavenLocal()
|
maven { url "file:///sdcard/.aide/maven/" }
|
||||||
// 或自定义本地仓库路径
|
|
||||||
maven { url "file:///sdcard/.m2/repository" }
|
|
||||||
|
|
||||||
// Nexus Maven 库地址
|
// Nexus Maven 库地址
|
||||||
// "WinBoLL Release"
|
// "WinBoLL Release"
|
||||||
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "cc.winboll.studio.timestamp"
|
applicationId "cc.winboll.studio.timestamp"
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
targetSdkVersion 30
|
targetSdkVersion 29
|
||||||
versionCode 1
|
versionCode 1
|
||||||
// versionName 更新后需要手动设置
|
// versionName 更新后需要手动设置
|
||||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Tue May 06 18:33:32 HKT 2025
|
#Wed May 07 14:44:06 HKT 2025
|
||||||
stageCount=5
|
stageCount=17
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.4
|
publishVersion=15.0.16
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.0.5
|
baseBetaVersion=15.0.17
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools" >
|
xmlns:tools="http://schemas.android.com/tools" >
|
||||||
|
|
||||||
<application>
|
<application
|
||||||
|
tools:replace="android:icon,android:roundIcon"
|
||||||
|
android:icon="@drawable/ic_launcher_beta"
|
||||||
|
android:roundIcon="@drawable/ic_launcher_beta">
|
||||||
|
|
||||||
<!-- Put flavor specific code here -->
|
<!-- Put flavor specific code here -->
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@drawable/ic_launcher_stage"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@drawable/ic_launcher_stage"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/MyAppTheme"
|
android:theme="@style/MyAppTheme"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ public class App extends GlobalApplication {
|
|||||||
//CrashHandler.getInstance().registerGlobal(this);
|
//CrashHandler.getInstance().registerGlobal(this);
|
||||||
//CrashHandler.getInstance().registerPart(this);
|
//CrashHandler.getInstance().registerPart(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getAPPIcon() {
|
||||||
|
return BuildConfig.DEBUG?R.drawable.ic_launcher_beta:R.drawable.ic_launcher_stage;
|
||||||
|
}
|
||||||
|
|
||||||
public static void write(InputStream input, OutputStream output) throws IOException {
|
public static void write(InputStream input, OutputStream output) throws IOException {
|
||||||
byte[] buf = new byte[1024 * 8];
|
byte[] buf = new byte[1024 * 8];
|
||||||
|
|||||||
@@ -26,11 +26,7 @@ import cc.winboll.studio.timestamp.receivers.ButtonClickReceiver;
|
|||||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
|
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
|
||||||
import cc.winboll.studio.timestamp.utils.NotificationHelper;
|
import cc.winboll.studio.timestamp.utils.NotificationHelper;
|
||||||
import cc.winboll.studio.timestamp.utils.ServiceUtil;
|
import cc.winboll.studio.timestamp.utils.ServiceUtil;
|
||||||
import cc.winboll.studio.timestamp.utils.TimeStampRemoteViewsUtil;
|
import cc.winboll.studio.timestamp.utils.TimeStampUtil;
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
public class MainService extends Service {
|
public class MainService extends Service {
|
||||||
|
|
||||||
@@ -59,10 +55,9 @@ public class MainService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
// 创建 RemoteViews 对象,并使用包含自定义 View 的布局
|
mNotificationHelper = new NotificationHelper();
|
||||||
//mRemoteViews = new RemoteViews(getPackageName(), R.layout.remoteviews_timestamp);
|
mNotificationHelper.createServiceNotificationChannel(this);
|
||||||
|
|
||||||
|
|
||||||
// 创建广播接收器实例
|
// 创建广播接收器实例
|
||||||
mButtonClickReceiver = new ButtonClickReceiver();
|
mButtonClickReceiver = new ButtonClickReceiver();
|
||||||
|
|
||||||
@@ -213,15 +208,10 @@ public class MainService extends Service {
|
|||||||
switch (message.what) {
|
switch (message.what) {
|
||||||
case MSG_UPDATE_TIMESTAMP:
|
case MSG_UPDATE_TIMESTAMP:
|
||||||
{
|
{
|
||||||
long currentMillis = System.currentTimeMillis();
|
String szTimeStampShowString = TimeStampUtil.getInstance(MainService.this).getTimeStampShowString();
|
||||||
Instant instant = Instant.ofEpochMilli(currentMillis);
|
mNotificationHelper.sendForegroundNotification(MainService.this, "时间戳:\n" + szTimeStampShowString + "\n已截取。");
|
||||||
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 + " 已复制");
|
|
||||||
|
|
||||||
LogUtils.d(TAG, "Hello, World! " + formattedDateTime);
|
LogUtils.d(TAG, "Hello, World! " + szTimeStampShowString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ public class AppConfigsModel extends BaseBean {
|
|||||||
|
|
||||||
public AppConfigsModel() {
|
public AppConfigsModel() {
|
||||||
this.isEnableService = false;
|
this.isEnableService = false;
|
||||||
this.timeStampFormatString = "yyyy-MM-dd HH:mm:ss";
|
this.timeStampFormatString = "yyyy-MM-dd HH:mm:ss SSS";
|
||||||
this.timeStampCopyFormatString = "yyyy_MM_dd-HH_mm_ss";
|
this.timeStampCopyFormatString = "yyyy_MM_dd-HH_mm_ss-SSS";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeStampCopyFormatString(String timeStampCopyFormatString) {
|
public void setTimeStampCopyFormatString(String timeStampCopyFormatString) {
|
||||||
|
|||||||
@@ -10,36 +10,27 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import cc.winboll.studio.timestamp.BuildConfig;
|
||||||
import cc.winboll.studio.timestamp.MainService;
|
import cc.winboll.studio.timestamp.MainService;
|
||||||
import cc.winboll.studio.timestamp.utils.AppConfigsUtil;
|
|
||||||
import cc.winboll.studio.timestamp.utils.ClipboardUtil;
|
import cc.winboll.studio.timestamp.utils.ClipboardUtil;
|
||||||
import java.time.Instant;
|
import cc.winboll.studio.timestamp.utils.TimeStampUtil;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
public class ButtonClickReceiver extends BroadcastReceiver {
|
public class ButtonClickReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
public static final String TAG = "ButtonClickReceiver";
|
public static final String TAG = "ButtonClickReceiver";
|
||||||
|
|
||||||
public static final String BUTTON_COPYTIMESTAMP_ACTION = "cc.winboll.studio.timestamp.receivers.ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION";
|
public static final String BUTTON_COPYTIMESTAMP_ACTION = ButtonClickReceiver.class.getName() + (BuildConfig.DEBUG ?".DEBUG_": ".") + "BUTTON_COPYTIMESTAMP_ACTION";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
LogUtils.d(TAG, "onReceive");
|
LogUtils.d(TAG, "onReceive");
|
||||||
if (intent.getAction().equals(BUTTON_COPYTIMESTAMP_ACTION)) {
|
if (intent.getAction().equals(BUTTON_COPYTIMESTAMP_ACTION)) {
|
||||||
// 在这里编写按钮点击后要执行的代码
|
// 在这里编写按钮点击后要执行的代码
|
||||||
long currentMillis = System.currentTimeMillis();
|
TimeStampUtil.getInstance(context).genTimeStamp();
|
||||||
Instant instant = Instant.ofEpochMilli(currentMillis);
|
ClipboardUtil.copyTextToClipboard(context, TimeStampUtil.getInstance(context).getTimeStampCopyString());
|
||||||
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);
|
|
||||||
|
|
||||||
// 比如显示一个Toast
|
// 比如显示一个Toast
|
||||||
Toast.makeText(context, formattedDateTime + " 已复制", Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, "时间戳:\n" + TimeStampUtil.getInstance(context).getTimeStampCopyString() + "\n已拷贝到剪贴板。", Toast.LENGTH_SHORT).show();
|
||||||
MainService.updateCopiedTimeStamp();
|
MainService.updateCopiedTimeStamp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ public class AppConfigsUtil {
|
|||||||
|
|
||||||
AppConfigsUtil(Context context) {
|
AppConfigsUtil(Context context) {
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
|
loadAppConfigs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized static AppConfigsUtil getInstance(Context context){
|
public synchronized static AppConfigsUtil getInstance(Context context){
|
||||||
if(_AppConfigsUtil == null) {
|
if(_AppConfigsUtil == null) {
|
||||||
_AppConfigsUtil = new AppConfigsUtil(context);
|
_AppConfigsUtil = new AppConfigsUtil(context);
|
||||||
_AppConfigsUtil.loadAppConfigs();
|
|
||||||
}
|
}
|
||||||
return _AppConfigsUtil;
|
return _AppConfigsUtil;
|
||||||
}
|
}
|
||||||
@@ -42,8 +42,8 @@ public class AppConfigsUtil {
|
|||||||
if (appConfigsModel != null) {
|
if (appConfigsModel != null) {
|
||||||
mAppConfigsModel = appConfigsModel;
|
mAppConfigsModel = appConfigsModel;
|
||||||
} else {
|
} else {
|
||||||
saveAppConfigs(new AppConfigsModel());
|
mAppConfigsModel = new AppConfigsModel();
|
||||||
_AppConfigsUtil = this;
|
saveAppConfigs(mAppConfigsModel);
|
||||||
}
|
}
|
||||||
return mAppConfigsModel;
|
return mAppConfigsModel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package cc.winboll.studio.timestamp.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen
|
||||||
|
* @Date 2025/05/07 02:38
|
||||||
|
* @Describe AudioPlayer
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class AudioPlayerMP3Util {
|
||||||
|
|
||||||
|
public static final String TAG = "AudioPlayer";
|
||||||
|
|
||||||
|
private static MediaPlayer mediaPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放指定的 MP3 文件
|
||||||
|
*
|
||||||
|
* @param context 上下文
|
||||||
|
* @param mp3FilePath MP3 文件的路径,例如:"/storage/emulated/0/Music/song.mp3"
|
||||||
|
*/
|
||||||
|
public static void playMp3(Context context, String mp3FilePath) {
|
||||||
|
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
|
||||||
|
mediaPlayer.stop();
|
||||||
|
mediaPlayer.release();
|
||||||
|
mediaPlayer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mediaPlayer = new MediaPlayer();
|
||||||
|
Uri uri = Uri.parse(mp3FilePath);
|
||||||
|
mediaPlayer.setDataSource(context, uri);
|
||||||
|
mediaPlayer.prepare();
|
||||||
|
mediaPlayer.start();
|
||||||
|
|
||||||
|
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||||
|
@Override
|
||||||
|
public void onCompletion(MediaPlayer mp) {
|
||||||
|
releaseMediaPlayer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||||
|
Log.e("AudioPlayer", "播放音频时出错: what=" + what + ", extra=" + extra);
|
||||||
|
releaseMediaPlayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
releaseMediaPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放 MediaPlayer 资源
|
||||||
|
*/
|
||||||
|
private static void releaseMediaPlayer() {
|
||||||
|
if (mediaPlayer != null) {
|
||||||
|
mediaPlayer.stop();
|
||||||
|
mediaPlayer.release();
|
||||||
|
mediaPlayer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package cc.winboll.studio.timestamp.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen
|
||||||
|
* @Date 2025/05/07 02:31
|
||||||
|
* @Describe AudioPlayerUtil
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class AudioPlayerUriUtil {
|
||||||
|
|
||||||
|
public static final String TAG = "AudioPlayerUtil";
|
||||||
|
|
||||||
|
private static MediaPlayer mediaPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放指定Uri的音频
|
||||||
|
* @param context 上下文
|
||||||
|
* @param audioUri 音频的Uri
|
||||||
|
*/
|
||||||
|
public static void playAudio(Context context, Uri audioUri) {
|
||||||
|
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
|
||||||
|
mediaPlayer.stop();
|
||||||
|
mediaPlayer.release();
|
||||||
|
mediaPlayer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mediaPlayer = new MediaPlayer();
|
||||||
|
mediaPlayer.setDataSource(context, audioUri);
|
||||||
|
mediaPlayer.prepare();
|
||||||
|
mediaPlayer.start();
|
||||||
|
|
||||||
|
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||||
|
@Override
|
||||||
|
public void onCompletion(MediaPlayer mp) {
|
||||||
|
releaseMediaPlayer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||||
|
Log.e("AudioPlayer", "播放音频时出错: what=" + what + ", extra=" + extra);
|
||||||
|
releaseMediaPlayer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
releaseMediaPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放MediaPlayer资源
|
||||||
|
*/
|
||||||
|
private static void releaseMediaPlayer() {
|
||||||
|
if (mediaPlayer != null) {
|
||||||
|
mediaPlayer.stop();
|
||||||
|
mediaPlayer.release();
|
||||||
|
mediaPlayer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -12,212 +12,195 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.BitmapFactory;
|
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 android.widget.RemoteViews;
|
||||||
import androidx.annotation.RequiresApi;
|
import cc.winboll.studio.timestamp.App;
|
||||||
import androidx.core.app.NotificationCompat;
|
import cc.winboll.studio.timestamp.MainActivity;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.timestamp.MainService;
|
||||||
import cc.winboll.studio.timestamp.R;
|
import cc.winboll.studio.timestamp.R;
|
||||||
import java.util.HashMap;
|
import cc.winboll.studio.timestamp.receivers.ButtonClickReceiver;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class NotificationHelper {
|
public class NotificationHelper {
|
||||||
public static final String TAG = "NotificationHelper";
|
|
||||||
|
|
||||||
// 渠道ID和名称
|
public static final String TAG = "NotificationUtil";
|
||||||
private static final String CHANNEL_ID_FOREGROUND = "foreground_channel";
|
public static final int ID_MSG_SERVICE = 10000;
|
||||||
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
|
static final String szSMSChannelID = "1";
|
||||||
public static final int FOREGROUND_NOTIFICATION_ID = 1001;
|
|
||||||
public static final int TEMPORARY_NOTIFICATION_ID = 2001;
|
|
||||||
|
|
||||||
private final Context mContext;
|
static final String szServiceChannelID = "0";
|
||||||
private final NotificationManager mNotificationManager;
|
|
||||||
|
|
||||||
// 示例:维护当前使用的渠道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;
|
mContext = context;
|
||||||
mNotificationManager = context.getSystemService(NotificationManager.class);
|
//创建通知渠道ID
|
||||||
|
String channelId = szServiceChannelID;
|
||||||
// 初始化配置
|
//创建通知渠道名称
|
||||||
activeChannelConfigs.put(
|
String channelName = "Service Message";
|
||||||
CHANNEL_ID_FOREGROUND,
|
//创建通知渠道重要性
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
int importance = NotificationManager.IMPORTANCE_MIN;
|
||||||
);
|
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
|
||||||
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.setSound(null, null);
|
||||||
channel.enableVibration(false);
|
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||||
mNotificationManager.createNotificationChannel(channel);
|
Context.NOTIFICATION_SERVICE);
|
||||||
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
return notificationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
public NotificationManager createSMSNotificationChannel(Context context) {
|
||||||
private void createTemporaryChannel() {
|
//创建通知渠道ID
|
||||||
NotificationChannel channel = new NotificationChannel(
|
String channelId = szSMSChannelID;
|
||||||
CHANNEL_ID_TEMPORARY,
|
//创建通知渠道名称
|
||||||
CHANNEL_NAME_TEMPORARY,
|
String channelName = "SMS Message";
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
//创建通知渠道重要性
|
||||||
);
|
int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||||
channel.setDescription("Temporary alert notifications");
|
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
|
||||||
channel.setSound(null, null);
|
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), Notification.AUDIO_ATTRIBUTES_DEFAULT);
|
||||||
channel.enableVibration(true);
|
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||||
channel.setVibrationPattern(new long[]{100, 200, 300, 400});
|
Context.NOTIFICATION_SERVICE);
|
||||||
channel.setBypassDnd(true);
|
notificationManager.createNotificationChannel(channel);
|
||||||
mNotificationManager.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)
|
//这里放一个count用来区分每一个通知
|
||||||
.setSmallIcon(R.drawable.ic_launcher)
|
//intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||||
.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);
|
//参数1:context 上下文对象
|
||||||
return notification;
|
//参数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);
|
||||||
|
|
||||||
// 显示常驻通知(通常用于前台服务)
|
Notification mForegroundNotification = new Notification.Builder(service, szServiceChannelID)
|
||||||
public Notification showCustomForegroundNotification(Intent intent, RemoteViews contentView, RemoteViews bigContentView) {
|
|
||||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
|
||||||
|
|
||||||
|
|
||||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_FOREGROUND)
|
|
||||||
.setSmallIcon(R.drawable.ic_launcher)
|
|
||||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
|
||||||
//.setContentTitle(title)
|
|
||||||
.setContentIntent(pendingIntent)
|
|
||||||
.setContent(contentView)
|
|
||||||
.setCustomBigContentView(bigContentView)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setOngoing(true)
|
//.setContentTitle(nessageNotificationBean.getTitle())
|
||||||
|
//.setContentText(nessageNotificationBean.getContent())
|
||||||
|
//.setContent(remoteviews)
|
||||||
|
.setWhen(System.currentTimeMillis())
|
||||||
|
.setSmallIcon(App.getAPPIcon())
|
||||||
|
//设置红色
|
||||||
|
.setColor(Color.parseColor("#F00606"))
|
||||||
|
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), App.getAPPIcon()))
|
||||||
|
.setContentIntent(mForegroundPendingIntent)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
mNotificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification);
|
// 创建 RemoteViews 对象,加载布局
|
||||||
return notification;
|
RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification_layout);
|
||||||
}
|
|
||||||
|
|
||||||
// 显示临时通知(自动消失)
|
// 自定义 TextView 的文本
|
||||||
public void showTemporaryNotification(Intent intent, String title, String content) {
|
remoteViews.setTextViewText(R.id.tv_timestamp, message);
|
||||||
showTemporaryNotification(intent, TEMPORARY_NOTIFICATION_ID, title, content);
|
// 自定义 TextView 的文本颜色
|
||||||
}
|
remoteViews.setTextColor(R.id.tv_timestamp, mContext.getResources().getColor(R.color.colorAccent, null));
|
||||||
|
// 这里虽然不能直接设置字体大小,但可以通过反射等方式尝试(不推荐,且有兼容性问题)
|
||||||
|
|
||||||
// 显示临时通知(自动消失)
|
// 设置按钮图片
|
||||||
public void showTemporaryNotification(Intent intent, int notificationID, String title, String content) {
|
remoteViews.setImageViewResource(R.id.iv_copytimestamp, App.getAPPIcon());
|
||||||
PendingIntent pendingIntent = createPendingIntent(intent);
|
// 创建点击通知后的意图
|
||||||
|
Intent intentMain = new Intent(mContext, MainActivity.class);
|
||||||
|
PendingIntent pendingMainIntent = PendingIntent.getActivity(mContext, 0, intentMain, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
// 设置通知的点击事件
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.tv_timestamp, pendingMainIntent);
|
||||||
|
|
||||||
Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
|
// 创建点击按钮后要发送的广播 Intent
|
||||||
.setSmallIcon(R.drawable.ic_launcher)
|
Intent broadcastIntent = new Intent(ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION);
|
||||||
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
|
android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(
|
||||||
.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,
|
mContext,
|
||||||
0,
|
0,
|
||||||
intent,
|
broadcastIntent,
|
||||||
flags
|
android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// public void sendSMSReceivedMessage(int notificationID, String szPhone, String szBody) {
|
// 为按钮设置点击事件
|
||||||
// Intent intent = new Intent(mContext, SMSActivity.class);
|
remoteViews.setOnClickPendingIntent(R.id.iv_copytimestamp, pendingIntent);
|
||||||
// intent.putExtra(SMSActivity.EXTRA_PHONE, szPhone);
|
|
||||||
// String szTitle = mContext.getString(R.string.text_smsfrom) + "<" + szPhone + ">";
|
mForegroundNotification.contentView = remoteViews;
|
||||||
// String szContent = "[ " + szBody + " ]";
|
mForegroundNotification.bigContentView = remoteViews;
|
||||||
// showTemporaryNotification(intent, notificationID, szTitle, szContent);
|
|
||||||
|
service.startForeground(ID_MSG_SERVICE, mForegroundNotification);
|
||||||
|
|
||||||
|
// 播放默认短信铃声
|
||||||
|
Uri defaultSmsRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||||
|
AudioPlayerUriUtil.playAudio(service, defaultSmsRingtoneUri);
|
||||||
|
|
||||||
|
// 播放应用铃声
|
||||||
|
// 获取MP3文件的Uri
|
||||||
|
Uri soundUri = Uri.parse("android.resource://" + service.getPackageName() + "/" + R.raw.diweiyi);
|
||||||
|
AudioPlayerUriUtil.playAudio(service, soundUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// public void sendSMSNotification(Context context, MessageNotificationBean messageNotificationBean) {
|
||||||
|
// NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||||
|
// Context.NOTIFICATION_SERVICE);
|
||||||
|
// /*NotificationManager notificationManager = createSMSNotificationChannel(context);
|
||||||
|
// if (notificationManager == null) {
|
||||||
|
// LogUtils.d(TAG, "createSMSNotificationChannel failed.");
|
||||||
|
// return;
|
||||||
|
// }*/
|
||||||
|
//
|
||||||
|
// //创建Notification,传入Context和channelId
|
||||||
|
// Intent intent = new Intent(context, SMSActivity.class);
|
||||||
|
// intent.putExtra(SMSActivity.EXTRA_PHONE, messageNotificationBean.getPhone());
|
||||||
|
// LogUtils.d(TAG, "sendSMSNotification(...) message.getPhone() is : " + messageNotificationBean.getPhone());
|
||||||
|
// //Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
|
||||||
|
// //intent.setClass(context, MainActivity.class);
|
||||||
|
// //这里放一个count用来区分每一个通知
|
||||||
|
// //intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
|
||||||
|
//
|
||||||
|
// //参数1:context 上下文对象
|
||||||
|
// //参数2:发送者私有的请求码(Private request code for the sender)
|
||||||
|
// //参数3:intent 意图对象
|
||||||
|
// //参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
|
||||||
|
// PendingIntent mRemindPendingIntent = PendingIntent.getActivity(context, messageNotificationBean.getMessageId(), intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
// Notification mSMSNotification = new Notification.Builder(context, szSMSChannelID)
|
||||||
|
// .setAutoCancel(true)
|
||||||
|
// .setContentTitle(messageNotificationBean.getTitle())
|
||||||
|
// .setContentText(messageNotificationBean.getContent())
|
||||||
|
// .setWhen(System.currentTimeMillis())
|
||||||
|
// .setSmallIcon(R.drawable.ic_launcher)
|
||||||
|
// //设置红色
|
||||||
|
// .setColor(Color.parseColor("#F00606"))
|
||||||
|
// .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher))
|
||||||
|
// .setContentIntent(mRemindPendingIntent)
|
||||||
|
// .build();
|
||||||
|
//
|
||||||
|
// RemoteViews mrvSMSNotificationView = new RemoteViews(context.getPackageName(), R.layout.remoteview);
|
||||||
|
// mrvSMSNotificationView.setTextViewText(R.id.remoteviewTextView1, messageNotificationBean.getTitle());
|
||||||
|
// mrvSMSNotificationView.setTextViewText(R.id.remoteviewTextView2, messageNotificationBean.getContent());
|
||||||
|
// mrvSMSNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
|
||||||
|
// mSMSNotification.contentView = mrvSMSNotificationView;
|
||||||
|
// mSMSNotification.bigContentView = mrvSMSNotificationView;
|
||||||
|
// notificationManager.notify(messageNotificationBean.getMessageId(), mSMSNotification);
|
||||||
|
// LogUtils.d(TAG, "getMessageId is : " + Integer.toString(messageNotificationBean.getMessageId()));
|
||||||
|
//
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public void cleanOldChannels() {
|
// public void sendSMSReceivedMessage(Context context, int nMessageId, String szPhone, String szBody) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
// String szTitle = context.getString(R.string.text_smsfrom) + "<" + szPhone + ">";
|
||||||
List<NotificationChannel> allChannels = mNotificationManager.getNotificationChannels();
|
// String szContent = "[ " + szBody + " ]";
|
||||||
for (NotificationChannel channel : allChannels) {
|
// sendSMSNotification(context, new MessageNotificationBean(nMessageId, szPhone, szTitle, szContent));
|
||||||
LogUtils.d(TAG, "Clean channel : " + channel.getId());
|
// }
|
||||||
if (!activeChannelConfigs.containsKey(channel.getId())) {
|
|
||||||
// 安全删除渠道
|
// public static void cancelNotification(Context context, int notificationId) {
|
||||||
mNotificationManager.deleteNotificationChannel(channel.getId());
|
// // 获取 NotificationManager 实例
|
||||||
LogUtils.d(TAG, String.format("Deleted Channel %s", channel.getId()));
|
// NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
}
|
// // 撤回指定 ID 的通知栏消息
|
||||||
}
|
// notificationManager.cancel(notificationId);
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
package cc.winboll.studio.timestamp.utils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author ZhanGSKen
|
|
||||||
* @Date 2025/05/05 21:10
|
|
||||||
* @Describe TimeStampRemoteViewsUtil
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.widget.RemoteViews;
|
|
||||||
import cc.winboll.studio.timestamp.MainActivity;
|
|
||||||
import cc.winboll.studio.timestamp.R;
|
|
||||||
import cc.winboll.studio.timestamp.receivers.ButtonClickReceiver;
|
|
||||||
|
|
||||||
public class TimeStampRemoteViewsUtil {
|
|
||||||
|
|
||||||
public static final String TAG = "TimeStampRemoteViewsUtil";
|
|
||||||
|
|
||||||
public static final String CHANNEL_ID = "TimeStampChannel";
|
|
||||||
|
|
||||||
static volatile TimeStampRemoteViewsUtil _TimeStampRemoteViewsUtil;
|
|
||||||
Context mContext;
|
|
||||||
NotificationHelper mNotificationHelper;
|
|
||||||
RemoteViews mRemoteViews;
|
|
||||||
Intent mIntentMain;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TimeStampRemoteViewsUtil(Context context) {
|
|
||||||
mContext = context;
|
|
||||||
mNotificationHelper = new NotificationHelper(context);
|
|
||||||
//createNotificationChannel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized TimeStampRemoteViewsUtil getInstance(Context context) {
|
|
||||||
if (_TimeStampRemoteViewsUtil == null) {
|
|
||||||
_TimeStampRemoteViewsUtil = new TimeStampRemoteViewsUtil(context);
|
|
||||||
}
|
|
||||||
return _TimeStampRemoteViewsUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// private void createNotificationChannel() {
|
|
||||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
// CharSequence name = "自定义视图通知通道";
|
|
||||||
// String description = "用于展示自定义视图的通知通道";
|
|
||||||
// int importance = NotificationManager.IMPORTANCE_HIGH;
|
|
||||||
// NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
|
|
||||||
// channel.setDescription(description);
|
|
||||||
// NotificationManager notificationManager = mContext.getSystemService(NotificationManager.class);
|
|
||||||
// notificationManager.createNotificationChannel(channel);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void showNotification(String msg) {
|
|
||||||
// if (mRemoteViews == null) {
|
|
||||||
// // 创建 RemoteViews 对象,加载布局
|
|
||||||
// mRemoteViews = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification_layout);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// // 自定义 TextView 的文本
|
|
||||||
// mRemoteViews.setTextViewText(R.id.tv_timestamp, msg);
|
|
||||||
// // 自定义 TextView 的文本颜色
|
|
||||||
// mRemoteViews.setTextColor(R.id.tv_timestamp, mContext.getResources().getColor(R.color.colorAccent, null));
|
|
||||||
// // 这里虽然不能直接设置字体大小,但可以通过反射等方式尝试(不推荐,且有兼容性问题)
|
|
||||||
//
|
|
||||||
// // 创建点击通知后的意图
|
|
||||||
// Intent intent = new Intent(mContext, MainActivity.class);
|
|
||||||
// PendingIntent pendingMainIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
// // 设置通知的点击事件
|
|
||||||
// mRemoteViews.setOnClickPendingIntent(R.id.tv_timestamp, pendingMainIntent);
|
|
||||||
//
|
|
||||||
// // 创建点击按钮后要发送的广播 Intent
|
|
||||||
// Intent broadcastIntent = new Intent(ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION);
|
|
||||||
// android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(
|
|
||||||
// mContext,
|
|
||||||
// 0,
|
|
||||||
// broadcastIntent,
|
|
||||||
// android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // 为按钮设置点击事件
|
|
||||||
// mRemoteViews.setOnClickPendingIntent(R.id.btn_copytimestamp, pendingIntent);
|
|
||||||
//
|
|
||||||
// // 构建通知
|
|
||||||
// NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID)
|
|
||||||
// .setSmallIcon(android.R.drawable.ic_dialog_info)
|
|
||||||
// .setContent(mRemoteViews)
|
|
||||||
// .setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
||||||
// .setOngoing(true)
|
|
||||||
// .setAutoCancel(true);
|
|
||||||
//
|
|
||||||
// // 显示通知
|
|
||||||
// NotificationManager notificationManager = mContext.getSystemService(NotificationManager.class);
|
|
||||||
// mNotification = builder.build();
|
|
||||||
// notificationManager.notify(1, mNotification);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public void showNotification(String msg) {
|
|
||||||
if (mRemoteViews == null) {
|
|
||||||
// 创建 RemoteViews 对象,加载布局
|
|
||||||
mRemoteViews = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification_layout);
|
|
||||||
}
|
|
||||||
// 自定义 TextView 的文本
|
|
||||||
mRemoteViews.setTextViewText(R.id.tv_timestamp, msg);
|
|
||||||
// 自定义 TextView 的文本颜色
|
|
||||||
mRemoteViews.setTextColor(R.id.tv_timestamp, mContext.getResources().getColor(R.color.colorAccent, null));
|
|
||||||
// 这里虽然不能直接设置字体大小,但可以通过反射等方式尝试(不推荐,且有兼容性问题)
|
|
||||||
|
|
||||||
// 创建点击通知后的意图
|
|
||||||
mIntentMain = new Intent(mContext, MainActivity.class);
|
|
||||||
PendingIntent pendingMainIntent = PendingIntent.getActivity(mContext, 0, mIntentMain, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
// 设置通知的点击事件
|
|
||||||
mRemoteViews.setOnClickPendingIntent(R.id.tv_timestamp, pendingMainIntent);
|
|
||||||
|
|
||||||
// 创建点击按钮后要发送的广播 Intent
|
|
||||||
Intent broadcastIntent = new Intent(ButtonClickReceiver.BUTTON_COPYTIMESTAMP_ACTION);
|
|
||||||
android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(
|
|
||||||
mContext,
|
|
||||||
0,
|
|
||||||
broadcastIntent,
|
|
||||||
android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
);
|
|
||||||
|
|
||||||
// 为按钮设置点击事件
|
|
||||||
mRemoteViews.setOnClickPendingIntent(R.id.btn_copytimestamp, pendingIntent);
|
|
||||||
|
|
||||||
mNotificationHelper.showCustomForegroundNotification(mIntentMain, mRemoteViews, mRemoteViews);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package cc.winboll.studio.timestamp.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen
|
||||||
|
* @Date 2025/05/07 11:03
|
||||||
|
* @Describe TimeStampUtil
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public class TimeStampUtil {
|
||||||
|
|
||||||
|
public static final String TAG = "TimeStampUtil";
|
||||||
|
|
||||||
|
volatile static TimeStampUtil _TimeStampUtil;
|
||||||
|
|
||||||
|
Context mContext;
|
||||||
|
long mTimeStamp;
|
||||||
|
|
||||||
|
TimeStampUtil(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
mTimeStamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static TimeStampUtil getInstance(Context context) {
|
||||||
|
if (_TimeStampUtil == null) {
|
||||||
|
_TimeStampUtil = new TimeStampUtil(context);
|
||||||
|
}
|
||||||
|
return _TimeStampUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void genTimeStamp() {
|
||||||
|
mTimeStamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTimeStampShowString() {
|
||||||
|
long currentMillis = mTimeStamp;
|
||||||
|
Instant instant = Instant.ofEpochMilli(currentMillis);
|
||||||
|
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||||
|
String szTimeStampFormatString = AppConfigsUtil.getInstance(mContext).getAppConfigsModel().getTimeStampFormatString();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(szTimeStampFormatString);
|
||||||
|
String formattedDateTime = ldt.format(formatter);
|
||||||
|
return formattedDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTimeStampCopyString() {
|
||||||
|
long currentMillis = mTimeStamp;
|
||||||
|
Instant instant = Instant.ofEpochMilli(currentMillis);
|
||||||
|
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||||
|
String szTimeStampFormatString = AppConfigsUtil.getInstance(mContext).getAppConfigsModel().getTimeStampCopyFormatString();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(szTimeStampFormatString);
|
||||||
|
String formattedDateTime = ldt.format(formatter);
|
||||||
|
return formattedDateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportHeight="108"
|
|
||||||
android:viewportWidth="108">
|
|
||||||
<path
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="78.5885"
|
|
||||||
android:endY="90.9159"
|
|
||||||
android:startX="48.7653"
|
|
||||||
android:startY="61.0927"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1" />
|
|
||||||
</vector>
|
|
||||||
16
timestamp/src/main/res/drawable/ic_launcher_beta.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:clickable="true">
|
||||||
|
<item
|
||||||
|
android:left="1dp"
|
||||||
|
android:top="1dp"
|
||||||
|
android:right="1dp"
|
||||||
|
android:bottom="1dp"
|
||||||
|
android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<item
|
||||||
|
android:left="1dp"
|
||||||
|
android:top="1dp"
|
||||||
|
android:right="1dp"
|
||||||
|
android:bottom="1dp"
|
||||||
|
android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</layer-list>
|
||||||
BIN
timestamp/src/main/res/drawable/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
11
timestamp/src/main/res/drawable/ic_launcher_stage.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:clickable="true">
|
||||||
|
<!--<item android:drawable="@drawable/ic_launcher_background"/>-->
|
||||||
|
<item
|
||||||
|
android:left="1dp"
|
||||||
|
android:top="1dp"
|
||||||
|
android:right="1dp"
|
||||||
|
android:bottom="1dp"
|
||||||
|
android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</layer-list>
|
||||||
@@ -28,21 +28,21 @@
|
|||||||
<Switch
|
<Switch
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="启用通知栏计时器"
|
android:text="启用时间戳常驻通知栏"
|
||||||
android:id="@+id/activitymainSwitch1"
|
android:id="@+id/activitymainSwitch1"
|
||||||
android:onClick="onSetMainServiceStatus"/>
|
android:onClick="onSetMainServiceStatus"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Show Format :"
|
android:text="Show Format Preview:"
|
||||||
android:paddingRight="10dp"/>
|
android:paddingRight="10dp"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
<Button
|
<Button
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="ApplySave"
|
android:text="ApplyShow"
|
||||||
android:id="@+id/btn_saveformatstring"
|
android:id="@+id/btn_saveformatstring"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:onClick="onSaveFormatString"/>
|
android:onClick="onSaveFormatString"/>
|
||||||
@@ -77,14 +77,14 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Copy Format :"
|
android:text="Copy Format Preview:"
|
||||||
android:paddingRight="10dp"/>
|
android:paddingRight="10dp"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -13,14 +13,13 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Text"
|
android:text="Text"
|
||||||
android:textColor="#000000"
|
android:textColor="#000000"
|
||||||
android:textSize="18sp"
|
android:textSize="14sp"
|
||||||
android:layout_weight="1.0"/>
|
android:layout_weight="1.0"/>
|
||||||
|
|
||||||
<Button
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="60dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="60dp"
|
||||||
android:text="复制时间戳"
|
android:id="@+id/iv_copytimestamp"/>
|
||||||
android:id="@+id/btn_copytimestamp"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 15 KiB |