diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 19fbd6a5..a44321d0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -72,6 +72,7 @@
+
-
\ No newline at end of file
+
diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/action/AlarmSetting.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/action/AlarmSetting.kt
index aba773cd..2e51f526 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/entity/action/AlarmSetting.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/entity/action/AlarmSetting.kt
@@ -5,7 +5,9 @@ import java.io.Serializable
data class AlarmSetting(
var description: String = "", //描述
var action: String = "stop", //动作: start=启动警报, stop=停止警报
- var volume: Int = 80, //播放音量
+ var volume: Int = 80, //播放音量,0=禁用
var playTimes: Int = 1, //播放次数,0=无限循环
var music: String = "", //音乐文件
+ var repeatTimes: Int = 5, //振动重复次数,0=禁用
+ var vibrate: String = "---___===___", //振动律动:=强振动, -弱震动, _不振动, 时长都是100ms
) : Serializable
diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/AlarmFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/AlarmFragment.kt
index cf2f6f62..559dab05 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/AlarmFragment.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/AlarmFragment.kt
@@ -20,6 +20,7 @@ import com.idormy.sms.forwarder.databinding.FragmentTasksActionAlarmBinding
import com.idormy.sms.forwarder.entity.MsgInfo
import com.idormy.sms.forwarder.entity.TaskSetting
import com.idormy.sms.forwarder.entity.action.AlarmSetting
+import com.idormy.sms.forwarder.utils.CommonUtils
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION
import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_ACTION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION
@@ -76,7 +77,7 @@ class AlarmFragment : BaseFragment(), View.OnC
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 2)
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
override fun onCountDown(time: Int) {
- binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time)
+ binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time.toString())
}
override fun onFinished() {
@@ -84,24 +85,55 @@ class AlarmFragment : BaseFragment(), View.OnC
}
})
+ binding!!.sbEnableMusic.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked) {
+ binding!!.layoutAlarmSettingsContent.visibility = View.VISIBLE
+ val volume = binding!!.xsbVolume.selectedNumber
+ if (volume == 0) {
+ binding!!.xsbVolume.setDefaultValue(80)
+ }
+ } else {
+ binding!!.layoutAlarmSettingsContent.visibility = View.GONE
+ binding!!.xsbVolume.setDefaultValue(0)
+ }
+ checkSetting(true)
+ }
+ binding!!.sbEnableVibrate.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked) {
+ binding!!.layoutVibrateSettingsContent.visibility = View.VISIBLE
+ val repeatTimes = binding!!.xsbRepeatTimes.selectedNumber
+ if (repeatTimes == 0) {
+ binding!!.xsbRepeatTimes.setDefaultValue(5)
+ }
+ } else {
+ binding!!.layoutVibrateSettingsContent.visibility = View.GONE
+ binding!!.xsbRepeatTimes.setDefaultValue(0)
+ }
+ checkSetting(true)
+ }
+
+ var settingVo = AlarmSetting()
Log.d(TAG, "initViews eventData:$eventData")
if (eventData != null) {
- val settingVo = Gson().fromJson(eventData, AlarmSetting::class.java)
+ settingVo = Gson().fromJson(eventData, AlarmSetting::class.java)
Log.d(TAG, "initViews settingVo:$settingVo")
if (settingVo.action == "start") {
binding!!.rgAlarmState.check(R.id.rb_start_alarm)
binding!!.layoutAlarmSettings.visibility = View.VISIBLE
+ binding!!.layoutVibrateSettings.visibility = View.VISIBLE
} else {
binding!!.rgAlarmState.check(R.id.rb_stop_alarm)
binding!!.layoutAlarmSettings.visibility = View.GONE
+ binding!!.layoutVibrateSettings.visibility = View.GONE
}
- binding!!.xsbVolume.setDefaultValue(settingVo.volume)
- binding!!.xsbLoopTimes.setDefaultValue(settingVo.playTimes)
- binding!!.etMusicPath.setText(settingVo.music)
- } else {
- binding!!.xsbVolume.setDefaultValue(80)
- binding!!.xsbLoopTimes.setDefaultValue(1)
}
+ binding!!.xsbVolume.setDefaultValue(settingVo.volume)
+ binding!!.xsbLoopTimes.setDefaultValue(settingVo.playTimes)
+ binding!!.etMusicPath.setText(settingVo.music)
+ binding!!.xsbRepeatTimes.setDefaultValue(settingVo.repeatTimes)
+ binding!!.etVibrationEffect.setText(settingVo.vibrate)
+ binding!!.sbEnableMusic.isChecked = settingVo.volume > 0
+ binding!!.sbEnableVibrate.isChecked = settingVo.repeatTimes > 0
}
override fun onDestroyView() {
@@ -125,12 +157,29 @@ class AlarmFragment : BaseFragment(), View.OnC
binding!!.layoutAlarmSettings.visibility = if (checkedId == R.id.rb_start_alarm) View.VISIBLE else View.GONE
checkSetting(true)
}
+ binding!!.btInsertVibrationEffect1.setOnClickListener(this)
+ binding!!.btInsertVibrationEffect2.setOnClickListener(this)
+ binding!!.btInsertVibrationEffect3.setOnClickListener(this)
}
@SingleClick
override fun onClick(v: View) {
try {
when (v.id) {
+ R.id.bt_insert_vibration_effect_1 -> {
+ CommonUtils.insertOrReplaceText2Cursor(binding!!.etVibrationEffect, "=")
+ return
+ }
+
+ R.id.bt_insert_vibration_effect_2 -> {
+ CommonUtils.insertOrReplaceText2Cursor(binding!!.etVibrationEffect, "-")
+ return
+ }
+
+ R.id.bt_insert_vibration_effect_3 -> {
+ CommonUtils.insertOrReplaceText2Cursor(binding!!.etVibrationEffect, "_")
+ return
+ }
R.id.btn_file_picker -> {
// 申请储存权限
@@ -173,6 +222,10 @@ class AlarmFragment : BaseFragment(), View.OnC
try {
val settingVo = checkSetting()
Log.d(TAG, settingVo.toString())
+ if (settingVo.volume == 0 && settingVo.repeatTimes == 0) {
+ XToastUtils.error(getString(R.string.alarm_settings_error))
+ return
+ }
val taskAction = TaskSetting(TASK_ACTION_ALARM, getString(R.string.task_alarm), settingVo.description, Gson().toJson(settingVo), requestCode)
val taskActionsJson = Gson().toJson(arrayListOf(taskAction))
val msgInfo = MsgInfo("task", getString(R.string.task_alarm), settingVo.description, Date(), getString(R.string.task_alarm))
@@ -212,6 +265,10 @@ class AlarmFragment : BaseFragment(), View.OnC
@SuppressLint("SetTextI18n")
override fun onGranted(permissions: List, all: Boolean) {
val settingVo = checkSetting()
+ if (settingVo.volume == 0 && settingVo.repeatTimes == 0) {
+ XToastUtils.error(getString(R.string.alarm_settings_error))
+ return
+ }
val intent = Intent()
intent.putExtra(KEY_BACK_DESCRIPTION_ACTION, settingVo.description)
intent.putExtra(KEY_BACK_DATA_ACTION, Gson().toJson(settingVo))
@@ -244,16 +301,27 @@ class AlarmFragment : BaseFragment(), View.OnC
@Suppress("SameParameterValue")
@SuppressLint("SetTextI18n")
private fun checkSetting(updateView: Boolean = false): AlarmSetting {
+ val enableMusic = binding!!.sbEnableMusic.isChecked
+ val enableVibrate = binding!!.sbEnableVibrate.isChecked
val volume = binding!!.xsbVolume.selectedNumber
val loopTimes = binding!!.xsbLoopTimes.selectedNumber
val music = binding!!.etMusicPath.text.toString().trim()
+ val repeatTimes = binding!!.xsbRepeatTimes.selectedNumber
+ val vibrationEffect = binding!!.etVibrationEffect.text.toString().trim()
val description = StringBuilder()
val action = if (binding!!.rgAlarmState.checkedRadioButtonId == R.id.rb_start_alarm) {
description.append(getString(R.string.start_alarm))
- description.append(", ").append(getString(R.string.alarm_volume)).append(":").append(volume).append("%")
- description.append(", ").append(getString(R.string.alarm_play_times)).append(":").append(loopTimes)
- if (music.isNotEmpty()) {
- description.append(", ").append(getString(R.string.alarm_music)).append(":").append(music)
+ if (enableMusic) {
+ description.append(", ").append(getString(R.string.alarm_volume)).append(":").append(volume).append("%")
+ description.append(", ").append(getString(R.string.alarm_play_times)).append(":").append(loopTimes)
+ if (music.isNotEmpty()) {
+ description.append(", ").append(getString(R.string.alarm_music)).append(":").append(music)
+ }
+ }
+ if (enableVibrate) {
+ vibrationEffect.ifEmpty { "---___===___".also { binding!!.etVibrationEffect.setText(it) } }
+ description.append(", ").append(getString(R.string.alarm_vibration_effect)).append(":").append(vibrationEffect)
+ description.append(", ").append(getString(R.string.alarm_repeat_times)).append(":").append(repeatTimes)
}
"start"
} else {
@@ -265,7 +333,7 @@ class AlarmFragment : BaseFragment(), View.OnC
binding!!.tvDescription.text = description.toString()
}
- return AlarmSetting(description.toString(), action, volume, loopTimes, music)
+ return AlarmSetting(description.toString(), action, volume, loopTimes, music, repeatTimes, vibrationEffect)
}
private fun findAudioFiles(directoryPath: String): List {
@@ -286,4 +354,4 @@ class AlarmFragment : BaseFragment(), View.OnC
val supportedExtensions = listOf("mp3", "ogg", "wav")
return supportedExtensions.any { it.equals(file.extension, ignoreCase = true) }
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt b/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt
index fd7a345f..70cfd9cd 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/service/ForegroundService.kt
@@ -40,6 +40,7 @@ import com.idormy.sms.forwarder.utils.INTENT_FRPC_APPLY_FILE
import com.idormy.sms.forwarder.utils.Log
import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CRON
+import com.idormy.sms.forwarder.utils.VibrationUtils
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
import com.idormy.sms.forwarder.workers.LoadAppListWorker
import com.jeremyliao.liveeventbus.LiveEventBus
@@ -92,77 +93,96 @@ class ForegroundService : Service() {
})
}
+ // 振动控制
+ private lateinit var vibrationUtils: VibrationUtils
+ private var isVibrating = false
+
+ // 音乐播放器
private var alarmPlayer: MediaPlayer? = null
private var alarmPlayTimes = 0
private val alarmObserver = Observer { alarm ->
Log.d(TAG, "Received alarm: $alarm")
+ //停止振动
+ if (vibrationUtils.isVibrating) {
+ vibrationUtils.stopVibration()
+ }
+ //停止播放音乐
alarmPlayer?.release()
alarmPlayer = null
if (alarm.action == "start") {
- //获取音量
- val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
- val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
- val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
- Log.d(TAG, "maxVolume=$maxVolume, currentVolume=$currentVolume")
- //设置音量
- audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (maxVolume * alarm.volume / 100), 0)
//播放音乐
- alarmPlayer = MediaPlayer().apply {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- val audioAttributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()
- setAudioAttributes(audioAttributes)
- } else {
- // 对于 Android 5.0 之前的版本,使用 setAudioStreamType
- val audioStreamType = AudioManager.STREAM_ALARM
- setAudioStreamType(audioStreamType)
- }
-
- try {
- if (alarm.music.isEmpty() || !File(alarm.music).exists()) {
- val fd = resources.openRawResourceFd(R.raw.alarm)
- setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
+ if (alarm.volume > 0) {
+ //获取音量
+ val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
+ val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
+ val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
+ Log.d(TAG, "maxVolume=$maxVolume, currentVolume=$currentVolume")
+ //设置音量
+ audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (maxVolume * alarm.volume / 100), 0)
+ //播放音乐
+ alarmPlayer = MediaPlayer().apply {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val audioAttributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()
+ setAudioAttributes(audioAttributes)
} else {
- setDataSource(alarm.music)
+ // 对于 Android 5.0 之前的版本,使用 setAudioStreamType
+ val audioStreamType = AudioManager.STREAM_ALARM
+ setAudioStreamType(audioStreamType)
}
- setOnPreparedListener {
- Log.d(TAG, "MediaPlayer prepared")
- start()
- alarmPlayTimes++
- //更新通知栏
- updateNotification(alarm.description, R.drawable.auto_task_icon_alarm, true)
- }
+ try {
+ if (alarm.music.isEmpty() || !File(alarm.music).exists()) {
+ val fd = resources.openRawResourceFd(R.raw.alarm)
+ setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
+ } else {
+ setDataSource(alarm.music)
+ }
- setOnCompletionListener {
- Log.d(TAG, "MediaPlayer completed")
- if (alarm.playTimes == 0 || alarmPlayTimes < alarm.playTimes) {
+ setOnPreparedListener {
+ Log.d(TAG, "MediaPlayer prepared")
start()
alarmPlayTimes++
- } else {
- stop()
- reset()
- release()
- alarmPlayer = null
- alarmPlayTimes = 0
- //恢复音量
- audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0)
- //恢复通知栏
- updateNotification(SettingUtils.notifyContent)
+ //更新通知栏
+ updateNotification(alarm.description, R.drawable.auto_task_icon_alarm, true)
}
- }
- setOnErrorListener { _, what, extra ->
- Log.e(TAG, "MediaPlayer error: what=$what, extra=$extra")
- release()
- return@setOnErrorListener true
- }
+ setOnCompletionListener {
+ Log.d(TAG, "MediaPlayer completed")
+ if (alarm.playTimes == 0 || alarmPlayTimes < alarm.playTimes) {
+ start()
+ alarmPlayTimes++
+ } else {
+ stop()
+ reset()
+ release()
+ alarmPlayer = null
+ alarmPlayTimes = 0
+ //恢复音量
+ audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0)
+ //恢复通知栏
+ updateNotification(SettingUtils.notifyContent)
+ }
+ }
- setVolume(alarm.volume / 100F, alarm.volume / 100F)
- prepareAsync()
- } catch (e: Exception) {
- Log.e(TAG, "MediaPlayer Exception: ${e.message}")
+ setOnErrorListener { _, what, extra ->
+ Log.e(TAG, "MediaPlayer error: what=$what, extra=$extra")
+ release()
+ return@setOnErrorListener true
+ }
+
+ setVolume(alarm.volume / 100F, alarm.volume / 100F)
+ prepareAsync()
+ } catch (e: Exception) {
+ Log.e(TAG, "MediaPlayer Exception: ${e.message}")
+ }
}
}
+ //振动提醒
+ if (alarm.repeatTimes > 0) {
+ isVibrating = true
+ val patternString = alarm.vibrate.repeat(alarm.repeatTimes)
+ vibrationUtils.startVibration(patternString)
+ }
}
}
@@ -176,7 +196,11 @@ class ForegroundService : Service() {
//纯客户端模式
if (SettingUtils.enablePureClientMode) return
+ //创建通知渠道
createNotificationChannel()
+
+ //初始化振动
+ vibrationUtils = VibrationUtils(this)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -292,6 +316,10 @@ class ForegroundService : Service() {
isRunning = false
alarmPlayer?.release()
alarmPlayer = null
+ //停止振动
+ if (vibrationUtils.isVibrating) {
+ vibrationUtils.stopVibration()
+ }
} catch (e: Exception) {
handleException(e, "stopForegroundService")
}
@@ -354,4 +382,4 @@ class ForegroundService : Service() {
isRunning = false
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/VibrationUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/VibrationUtils.kt
new file mode 100644
index 00000000..36bf1017
--- /dev/null
+++ b/app/src/main/java/com/idormy/sms/forwarder/utils/VibrationUtils.kt
@@ -0,0 +1,77 @@
+package com.idormy.sms.forwarder.utils
+
+import android.content.Context
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import android.os.VibrationEffect
+import android.os.Vibrator
+
+@Suppress("DEPRECATION")
+class VibrationUtils(context: Context) {
+
+ private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ private val handler = Handler(Looper.getMainLooper())
+ var isVibrating = false
+ private set
+
+ fun startVibration(patternString: String) {
+ isVibrating = true
+ val parsedPattern = parsePattern(patternString)
+ vibratePattern(parsedPattern, 0)
+ }
+
+ fun stopVibration() {
+ isVibrating = false
+ vibrator.cancel()
+ }
+
+ private fun parsePattern(pattern: String): List> {
+ val parsedPattern = mutableListOf>()
+ var currentChar = pattern[0]
+ var currentLength = 1L
+
+ for (i in 1 until pattern.length) {
+ if (pattern[i] == currentChar) {
+ currentLength++
+ } else {
+ parsedPattern.add(createTriple(currentChar, currentLength))
+ currentChar = pattern[i]
+ currentLength = 1L
+ }
+ }
+ parsedPattern.add(createTriple(currentChar, currentLength))
+ return parsedPattern
+ }
+
+ private fun createTriple(char: Char, length: Long): Triple {
+ val duration = 100L * length
+ val intensity = when (char) {
+ '=' -> 255
+ '-' -> 128
+ '_' -> 0
+ else -> 0
+ }
+ return Triple(duration, intensity > 0, intensity)
+ }
+
+ private fun vibratePattern(parsedPattern: List>, index: Int) {
+ if (isVibrating && index < parsedPattern.size) {
+ val (duration, shouldVibrate, intensity) = parsedPattern[index]
+ if (shouldVibrate) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val effect = VibrationEffect.createOneShot(duration, intensity)
+ vibrator.vibrate(effect)
+ } else {
+ vibrator.vibrate(duration)
+ }
+ }
+ handler.postDelayed({
+ if (isVibrating) {
+ vibrator.cancel()
+ vibratePattern(parsedPattern, index + 1)
+ }
+ }, duration)
+ }
+ }
+}
diff --git a/app/src/main/res/layout/fragment_tasks_action_alarm.xml b/app/src/main/res/layout/fragment_tasks_action_alarm.xml
index fb61c6a6..4b4b1ea5 100644
--- a/app/src/main/res/layout/fragment_tasks_action_alarm.xml
+++ b/app/src/main/res/layout/fragment_tasks_action_alarm.xml
@@ -86,101 +86,241 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
-
-
-
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
-
-
+ android:text="@string/alarm_play_settings"
+ android:textStyle="bold"
+ tools:ignore="RelativeOverlap" />
+
+
+ android:orientation="vertical">
-
+
-
+ android:gravity="center_vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
-
-
+ android:text="@string/alarm_vibrate_settings"
+ android:textStyle="bold"
+ tools:ignore="RelativeOverlap" />
-
+
+
+
+
+
+
+
+
+ android:gravity="center_vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -220,4 +360,4 @@
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index 50e6fe31..ec6dead9 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -973,8 +973,8 @@
Control the enable/disable of "Rules"
Channels On/Off
Control the enable/disable of "Senders"
- Alarm
- Alarm
+ Alarm Reminder
+ Play music/vibrate phone to remind
Resend Message
Resend forwarded records since N hours ago, 0=ALL
Resend forwarding records since %s hours ago for %s
@@ -1124,11 +1124,19 @@
Start Alarm
Stop Alarm
- Playback Settings
+ Play Music
Specify Music
Opt., download mp3/ogg/wav to the Download directory.
Alarm Volume
Play Times(0=Infinite)
+ Vibrate Phone
+ Repeat Times
+ Vibration Effect
+ Syntax: =[strong], -[weak], _[no], 100ms each
+ Strong vibration
+ Weak vibration
+ No vibration
+ At least one of Play Music/Vibrate Phone must be enabled
%s tag is invalid: %s
Please input task name.
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 4e5c31ad..f6ccc67a 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -974,8 +974,8 @@
控制【转发规则】的启用/禁用
启停通道
控制【发送通道】的启用/禁用
- 声音警报
- 声音警报
+ 警报提醒
+ 播放音乐/振动手机提醒
重发消息
自动重发N小时以来的转发记录,0=全部
自动重发%s小时以来%s的转发记录
@@ -1125,11 +1125,19 @@
启动警报
停止警报
- 播放设置
+ 播放音乐
指定音乐
可选,下载 mp3/ogg/wav 到 Download 目录
播放音量
播放次数(0=无限)
+ 振动手机
+ 重复次数
+ 振动效果
+ 语法:=[强振动], -[弱震动], _[不振动], 每次100ms
+ 强振动 100ms
+ 弱振动 100ms
+ 不振动 100ms
+ 播放音乐/振动手机必须至少开启一个
%s 标签无效:%s
请输入任务名称
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index c269f5d9..ce817f49 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -967,8 +967,8 @@
控制【轉發規則】的啟用/禁用
啟停通道
控制【發送通道】的啟用/禁用
- 聲音警報
- 聲音警報
+ 警報提醒
+ 播放音樂/震動手機發出提醒
重發消息
自動重發N小時以來的轉發記錄,0=全部
自動重發%s小時以來%s的轉發記錄
@@ -1118,11 +1118,19 @@
啟動警報
停止警報
- 播放設置
+ 播放音樂
指定音樂
可選,下載 mp3/ogg/wav 到 Download 目錄
播放音量
播放次數(0=無限)
+ 振動手機
+ 重複次數
+ 振動效果
+ 語法:=[強振動], -[弱振動], _[不振動], 每次100ms
+ 強振動 100ms
+ 弱振動 100ms
+ 不振動 100ms
+ 播放音樂/振動手機必須至少開啟一個
%s 標籤無效:%s
請輸入任務名稱
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 351047be..7bb27279 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1001,8 +1001,8 @@
控制【转发规则】的启用/禁用
启停通道
控制【发送通道】的启用/禁用
- 声音警报
- 播放音乐提醒
+ 警报提醒
+ 播放音乐/振动手机提醒
重发消息
自动重发N小时以来的转发记录,0=全部
自动重发%s小时以来%s的转发记录
@@ -1152,11 +1152,19 @@
启动警报
停止警报
- 播放设置
+ 播放音乐
指定音乐
可选,下载 mp3/ogg/wav 到 Download 目录
播放音量
播放次数(0=无限)
+ 振动手机
+ 重复次数
+ 振动效果
+ 语法:=[强振动], -[弱震动], _[不振动], 每次100ms
+ 强振动 100ms
+ 弱振动 100ms
+ 不振动 100ms
+ 播放音乐/振动手机必须至少开启一个
%s 标签无效:%s
请输入任务名称
diff --git a/gradle.properties b/gradle.properties
index 4a72e88c..3a27ffdd 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -13,7 +13,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# org.gradle.parallel=true
# 是否打包APK,打正式包时请设置为true,使用正式的签名
isNeedPackage=true
-isNeedClean=true
+isNeedClean=false
# 是否排除Frpc动态库,打正式包时请设置为true
excludeFrpclib=true
# 是否使用leakcanary检测内存泄漏,打正式包时请设置为false