mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-02 17:07:41 +08:00
parent
c86f20c6de
commit
3a341f6c4e
@ -72,6 +72,7 @@
|
|||||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
@ -377,4 +378,4 @@
|
|||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -5,7 +5,9 @@ import java.io.Serializable
|
|||||||
data class AlarmSetting(
|
data class AlarmSetting(
|
||||||
var description: String = "", //描述
|
var description: String = "", //描述
|
||||||
var action: String = "stop", //动作: start=启动警报, stop=停止警报
|
var action: String = "stop", //动作: start=启动警报, stop=停止警报
|
||||||
var volume: Int = 80, //播放音量
|
var volume: Int = 80, //播放音量,0=禁用
|
||||||
var playTimes: Int = 1, //播放次数,0=无限循环
|
var playTimes: Int = 1, //播放次数,0=无限循环
|
||||||
var music: String = "", //音乐文件
|
var music: String = "", //音乐文件
|
||||||
|
var repeatTimes: Int = 5, //振动重复次数,0=禁用
|
||||||
|
var vibrate: String = "---___===___", //振动律动:=强振动, -弱震动, _不振动, 时长都是100ms
|
||||||
) : Serializable
|
) : Serializable
|
||||||
|
@ -20,6 +20,7 @@ import com.idormy.sms.forwarder.databinding.FragmentTasksActionAlarmBinding
|
|||||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||||
import com.idormy.sms.forwarder.entity.TaskSetting
|
import com.idormy.sms.forwarder.entity.TaskSetting
|
||||||
import com.idormy.sms.forwarder.entity.action.AlarmSetting
|
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_DATA_ACTION
|
||||||
import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_ACTION
|
import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_ACTION
|
||||||
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION
|
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION
|
||||||
@ -76,7 +77,7 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
|
|||||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 2)
|
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 2)
|
||||||
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||||
override fun onCountDown(time: Int) {
|
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() {
|
override fun onFinished() {
|
||||||
@ -84,24 +85,55 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), 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")
|
Log.d(TAG, "initViews eventData:$eventData")
|
||||||
if (eventData != null) {
|
if (eventData != null) {
|
||||||
val settingVo = Gson().fromJson(eventData, AlarmSetting::class.java)
|
settingVo = Gson().fromJson(eventData, AlarmSetting::class.java)
|
||||||
Log.d(TAG, "initViews settingVo:$settingVo")
|
Log.d(TAG, "initViews settingVo:$settingVo")
|
||||||
if (settingVo.action == "start") {
|
if (settingVo.action == "start") {
|
||||||
binding!!.rgAlarmState.check(R.id.rb_start_alarm)
|
binding!!.rgAlarmState.check(R.id.rb_start_alarm)
|
||||||
binding!!.layoutAlarmSettings.visibility = View.VISIBLE
|
binding!!.layoutAlarmSettings.visibility = View.VISIBLE
|
||||||
|
binding!!.layoutVibrateSettings.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
binding!!.rgAlarmState.check(R.id.rb_stop_alarm)
|
binding!!.rgAlarmState.check(R.id.rb_stop_alarm)
|
||||||
binding!!.layoutAlarmSettings.visibility = View.GONE
|
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() {
|
override fun onDestroyView() {
|
||||||
@ -125,12 +157,29 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
|
|||||||
binding!!.layoutAlarmSettings.visibility = if (checkedId == R.id.rb_start_alarm) View.VISIBLE else View.GONE
|
binding!!.layoutAlarmSettings.visibility = if (checkedId == R.id.rb_start_alarm) View.VISIBLE else View.GONE
|
||||||
checkSetting(true)
|
checkSetting(true)
|
||||||
}
|
}
|
||||||
|
binding!!.btInsertVibrationEffect1.setOnClickListener(this)
|
||||||
|
binding!!.btInsertVibrationEffect2.setOnClickListener(this)
|
||||||
|
binding!!.btInsertVibrationEffect3.setOnClickListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SingleClick
|
@SingleClick
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
try {
|
try {
|
||||||
when (v.id) {
|
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 -> {
|
R.id.btn_file_picker -> {
|
||||||
// 申请储存权限
|
// 申请储存权限
|
||||||
@ -173,6 +222,10 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
|
|||||||
try {
|
try {
|
||||||
val settingVo = checkSetting()
|
val settingVo = checkSetting()
|
||||||
Log.d(TAG, settingVo.toString())
|
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 taskAction = TaskSetting(TASK_ACTION_ALARM, getString(R.string.task_alarm), settingVo.description, Gson().toJson(settingVo), requestCode)
|
||||||
val taskActionsJson = Gson().toJson(arrayListOf(taskAction))
|
val taskActionsJson = Gson().toJson(arrayListOf(taskAction))
|
||||||
val msgInfo = MsgInfo("task", getString(R.string.task_alarm), settingVo.description, Date(), getString(R.string.task_alarm))
|
val msgInfo = MsgInfo("task", getString(R.string.task_alarm), settingVo.description, Date(), getString(R.string.task_alarm))
|
||||||
@ -212,6 +265,10 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
|
|||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||||
val settingVo = checkSetting()
|
val settingVo = checkSetting()
|
||||||
|
if (settingVo.volume == 0 && settingVo.repeatTimes == 0) {
|
||||||
|
XToastUtils.error(getString(R.string.alarm_settings_error))
|
||||||
|
return
|
||||||
|
}
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.putExtra(KEY_BACK_DESCRIPTION_ACTION, settingVo.description)
|
intent.putExtra(KEY_BACK_DESCRIPTION_ACTION, settingVo.description)
|
||||||
intent.putExtra(KEY_BACK_DATA_ACTION, Gson().toJson(settingVo))
|
intent.putExtra(KEY_BACK_DATA_ACTION, Gson().toJson(settingVo))
|
||||||
@ -244,16 +301,27 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
|
|||||||
@Suppress("SameParameterValue")
|
@Suppress("SameParameterValue")
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
private fun checkSetting(updateView: Boolean = false): AlarmSetting {
|
private fun checkSetting(updateView: Boolean = false): AlarmSetting {
|
||||||
|
val enableMusic = binding!!.sbEnableMusic.isChecked
|
||||||
|
val enableVibrate = binding!!.sbEnableVibrate.isChecked
|
||||||
val volume = binding!!.xsbVolume.selectedNumber
|
val volume = binding!!.xsbVolume.selectedNumber
|
||||||
val loopTimes = binding!!.xsbLoopTimes.selectedNumber
|
val loopTimes = binding!!.xsbLoopTimes.selectedNumber
|
||||||
val music = binding!!.etMusicPath.text.toString().trim()
|
val music = binding!!.etMusicPath.text.toString().trim()
|
||||||
|
val repeatTimes = binding!!.xsbRepeatTimes.selectedNumber
|
||||||
|
val vibrationEffect = binding!!.etVibrationEffect.text.toString().trim()
|
||||||
val description = StringBuilder()
|
val description = StringBuilder()
|
||||||
val action = if (binding!!.rgAlarmState.checkedRadioButtonId == R.id.rb_start_alarm) {
|
val action = if (binding!!.rgAlarmState.checkedRadioButtonId == R.id.rb_start_alarm) {
|
||||||
description.append(getString(R.string.start_alarm))
|
description.append(getString(R.string.start_alarm))
|
||||||
description.append(", ").append(getString(R.string.alarm_volume)).append(":").append(volume).append("%")
|
if (enableMusic) {
|
||||||
description.append(", ").append(getString(R.string.alarm_play_times)).append(":").append(loopTimes)
|
description.append(", ").append(getString(R.string.alarm_volume)).append(":").append(volume).append("%")
|
||||||
if (music.isNotEmpty()) {
|
description.append(", ").append(getString(R.string.alarm_play_times)).append(":").append(loopTimes)
|
||||||
description.append(", ").append(getString(R.string.alarm_music)).append(":").append(music)
|
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"
|
"start"
|
||||||
} else {
|
} else {
|
||||||
@ -265,7 +333,7 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
|
|||||||
binding!!.tvDescription.text = description.toString()
|
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<String> {
|
private fun findAudioFiles(directoryPath: String): List<String> {
|
||||||
@ -286,4 +354,4 @@ class AlarmFragment : BaseFragment<FragmentTasksActionAlarmBinding?>(), View.OnC
|
|||||||
val supportedExtensions = listOf("mp3", "ogg", "wav")
|
val supportedExtensions = listOf("mp3", "ogg", "wav")
|
||||||
return supportedExtensions.any { it.equals(file.extension, ignoreCase = true) }
|
return supportedExtensions.any { it.equals(file.extension, ignoreCase = true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.Log
|
||||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CRON
|
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.utils.task.CronJobScheduler
|
||||||
import com.idormy.sms.forwarder.workers.LoadAppListWorker
|
import com.idormy.sms.forwarder.workers.LoadAppListWorker
|
||||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
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 alarmPlayer: MediaPlayer? = null
|
||||||
private var alarmPlayTimes = 0
|
private var alarmPlayTimes = 0
|
||||||
private val alarmObserver = Observer<AlarmSetting> { alarm ->
|
private val alarmObserver = Observer<AlarmSetting> { alarm ->
|
||||||
Log.d(TAG, "Received alarm: $alarm")
|
Log.d(TAG, "Received alarm: $alarm")
|
||||||
|
//停止振动
|
||||||
|
if (vibrationUtils.isVibrating) {
|
||||||
|
vibrationUtils.stopVibration()
|
||||||
|
}
|
||||||
|
//停止播放音乐
|
||||||
alarmPlayer?.release()
|
alarmPlayer?.release()
|
||||||
alarmPlayer = null
|
alarmPlayer = null
|
||||||
if (alarm.action == "start") {
|
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 (alarm.volume > 0) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
//获取音量
|
||||||
val audioAttributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()
|
val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
|
||||||
setAudioAttributes(audioAttributes)
|
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||||
} else {
|
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||||
// 对于 Android 5.0 之前的版本,使用 setAudioStreamType
|
Log.d(TAG, "maxVolume=$maxVolume, currentVolume=$currentVolume")
|
||||||
val audioStreamType = AudioManager.STREAM_ALARM
|
//设置音量
|
||||||
setAudioStreamType(audioStreamType)
|
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (maxVolume * alarm.volume / 100), 0)
|
||||||
}
|
//播放音乐
|
||||||
|
alarmPlayer = MediaPlayer().apply {
|
||||||
try {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
if (alarm.music.isEmpty() || !File(alarm.music).exists()) {
|
val audioAttributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build()
|
||||||
val fd = resources.openRawResourceFd(R.raw.alarm)
|
setAudioAttributes(audioAttributes)
|
||||||
setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
|
|
||||||
} else {
|
} else {
|
||||||
setDataSource(alarm.music)
|
// 对于 Android 5.0 之前的版本,使用 setAudioStreamType
|
||||||
|
val audioStreamType = AudioManager.STREAM_ALARM
|
||||||
|
setAudioStreamType(audioStreamType)
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnPreparedListener {
|
try {
|
||||||
Log.d(TAG, "MediaPlayer prepared")
|
if (alarm.music.isEmpty() || !File(alarm.music).exists()) {
|
||||||
start()
|
val fd = resources.openRawResourceFd(R.raw.alarm)
|
||||||
alarmPlayTimes++
|
setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
|
||||||
//更新通知栏
|
} else {
|
||||||
updateNotification(alarm.description, R.drawable.auto_task_icon_alarm, true)
|
setDataSource(alarm.music)
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnCompletionListener {
|
setOnPreparedListener {
|
||||||
Log.d(TAG, "MediaPlayer completed")
|
Log.d(TAG, "MediaPlayer prepared")
|
||||||
if (alarm.playTimes == 0 || alarmPlayTimes < alarm.playTimes) {
|
|
||||||
start()
|
start()
|
||||||
alarmPlayTimes++
|
alarmPlayTimes++
|
||||||
} else {
|
//更新通知栏
|
||||||
stop()
|
updateNotification(alarm.description, R.drawable.auto_task_icon_alarm, true)
|
||||||
reset()
|
|
||||||
release()
|
|
||||||
alarmPlayer = null
|
|
||||||
alarmPlayTimes = 0
|
|
||||||
//恢复音量
|
|
||||||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0)
|
|
||||||
//恢复通知栏
|
|
||||||
updateNotification(SettingUtils.notifyContent)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setOnErrorListener { _, what, extra ->
|
setOnCompletionListener {
|
||||||
Log.e(TAG, "MediaPlayer error: what=$what, extra=$extra")
|
Log.d(TAG, "MediaPlayer completed")
|
||||||
release()
|
if (alarm.playTimes == 0 || alarmPlayTimes < alarm.playTimes) {
|
||||||
return@setOnErrorListener true
|
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)
|
setOnErrorListener { _, what, extra ->
|
||||||
prepareAsync()
|
Log.e(TAG, "MediaPlayer error: what=$what, extra=$extra")
|
||||||
} catch (e: Exception) {
|
release()
|
||||||
Log.e(TAG, "MediaPlayer Exception: ${e.message}")
|
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
|
if (SettingUtils.enablePureClientMode) return
|
||||||
|
|
||||||
|
//创建通知渠道
|
||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
|
|
||||||
|
//初始化振动
|
||||||
|
vibrationUtils = VibrationUtils(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
@ -292,6 +316,10 @@ class ForegroundService : Service() {
|
|||||||
isRunning = false
|
isRunning = false
|
||||||
alarmPlayer?.release()
|
alarmPlayer?.release()
|
||||||
alarmPlayer = null
|
alarmPlayer = null
|
||||||
|
//停止振动
|
||||||
|
if (vibrationUtils.isVibrating) {
|
||||||
|
vibrationUtils.stopVibration()
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
handleException(e, "stopForegroundService")
|
handleException(e, "stopForegroundService")
|
||||||
}
|
}
|
||||||
@ -354,4 +382,4 @@ class ForegroundService : Service() {
|
|||||||
isRunning = false
|
isRunning = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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<Triple<Long, Boolean, Int>> {
|
||||||
|
val parsedPattern = mutableListOf<Triple<Long, Boolean, Int>>()
|
||||||
|
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<Long, Boolean, Int> {
|
||||||
|
val duration = 100L * length
|
||||||
|
val intensity = when (char) {
|
||||||
|
'=' -> 255
|
||||||
|
'-' -> 128
|
||||||
|
'_' -> 0
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
return Triple(duration, intensity > 0, intensity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun vibratePattern(parsedPattern: List<Triple<Long, Boolean, Int>>, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -86,101 +86,241 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/alarm_play_settings"
|
|
||||||
android:textStyle="bold"
|
|
||||||
tools:ignore="RelativeOverlap" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:background="?attr/xui_config_color_separator_light" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/alarm_volume"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<com.xuexiang.xui.widget.picker.XSeekBar
|
|
||||||
android:id="@+id/xsb_volume"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:padding="0dp"
|
android:text="@string/alarm_play_settings"
|
||||||
app:xsb_max="100"
|
android:textStyle="bold"
|
||||||
app:xsb_min="1" />
|
tools:ignore="RelativeOverlap" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||||
|
android:id="@+id/sb_enable_music"
|
||||||
|
style="@style/SwitchButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_alarm_settings_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<View
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="1dp"
|
||||||
android:text="@string/alarm_play_times"
|
android:layout_marginTop="5dp"
|
||||||
android:textStyle="bold" />
|
android:background="?attr/xui_config_color_separator_light" />
|
||||||
|
|
||||||
<com.xuexiang.xui.widget.picker.XSeekBar
|
<LinearLayout
|
||||||
android:id="@+id/xsb_loop_times"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="5dp"
|
android:gravity="center_vertical">
|
||||||
android:layout_weight="1"
|
|
||||||
android:padding="0dp"
|
<TextView
|
||||||
app:xsb_max="30"
|
android:layout_width="wrap_content"
|
||||||
app:xsb_min="0" />
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/alarm_volume"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.picker.XSeekBar
|
||||||
|
android:id="@+id/xsb_volume"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="0dp"
|
||||||
|
app:xsb_max="100"
|
||||||
|
app:xsb_min="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/alarm_play_times"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.picker.XSeekBar
|
||||||
|
android:id="@+id/xsb_loop_times"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="0dp"
|
||||||
|
app:xsb_max="30"
|
||||||
|
app:xsb_min="0" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/alarm_music"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.edittext.ClearEditText
|
||||||
|
android:id="@+id/et_music_path"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/alarm_music_tips"
|
||||||
|
android:textSize="@dimen/text_size_mini"
|
||||||
|
tools:ignore="SmallSp" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||||
|
android:id="@+id/btn_file_picker"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:text="@string/select_file"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="@dimen/text_size_mini"
|
||||||
|
app:sb_color_unpressed="@color/colorPrimary"
|
||||||
|
app:sb_ripple_color="@color/white"
|
||||||
|
app:sb_ripple_duration="500"
|
||||||
|
app:sb_shape_type="rectangle"
|
||||||
|
tools:ignore="SmallSp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_vibrate_settings"
|
||||||
|
style="@style/BarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/alarm_music"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<com.xuexiang.xui.widget.edittext.ClearEditText
|
|
||||||
android:id="@+id/et_music_path"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:hint="@string/alarm_music_tips"
|
android:text="@string/alarm_vibrate_settings"
|
||||||
android:textSize="@dimen/text_size_mini"
|
android:textStyle="bold"
|
||||||
tools:ignore="SmallSp" />
|
tools:ignore="RelativeOverlap" />
|
||||||
|
|
||||||
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||||
android:id="@+id/btn_file_picker"
|
android:id="@+id/sb_enable_vibrate"
|
||||||
|
style="@style/SwitchButtonStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_vibrate_settings_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:background="?attr/xui_config_color_separator_light" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="5dp"
|
android:gravity="center_vertical">
|
||||||
android:gravity="center"
|
|
||||||
android:padding="5dp"
|
<TextView
|
||||||
android:text="@string/select_file"
|
android:layout_width="wrap_content"
|
||||||
android:textColor="@color/white"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="@dimen/text_size_mini"
|
android:text="@string/alarm_vibration_effect"
|
||||||
app:sb_color_unpressed="@color/colorPrimary"
|
android:textStyle="bold" />
|
||||||
app:sb_ripple_color="@color/white"
|
|
||||||
app:sb_ripple_duration="500"
|
<com.xuexiang.xui.widget.edittext.ClearEditText
|
||||||
app:sb_shape_type="rectangle"
|
android:id="@+id/et_vibration_effect"
|
||||||
tools:ignore="SmallSp" />
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/alarm_vibration_effect_tips"
|
||||||
|
android:textSize="@dimen/text_size_mini"
|
||||||
|
tools:ignore="SmallSp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||||
|
android:id="@+id/bt_insert_vibration_effect_1"
|
||||||
|
style="@style/insertButtonStyle"
|
||||||
|
android:text="@string/alarm_vibration_effect_1" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||||
|
android:id="@+id/bt_insert_vibration_effect_2"
|
||||||
|
style="@style/insertButtonStyle"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:text="@string/alarm_vibration_effect_2" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||||
|
android:id="@+id/bt_insert_vibration_effect_3"
|
||||||
|
style="@style/insertButtonStyle"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:text="@string/alarm_vibration_effect_3" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/alarm_repeat_times"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.xuexiang.xui.widget.picker.XSeekBar
|
||||||
|
android:id="@+id/xsb_repeat_times"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="0dp"
|
||||||
|
app:xsb_max="30"
|
||||||
|
app:xsb_min="0" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -220,4 +360,4 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -973,8 +973,8 @@
|
|||||||
<string name="task_rule_tips">Control the enable/disable of "Rules"</string>
|
<string name="task_rule_tips">Control the enable/disable of "Rules"</string>
|
||||||
<string name="task_sender">Channels On/Off</string>
|
<string name="task_sender">Channels On/Off</string>
|
||||||
<string name="task_sender_tips">Control the enable/disable of "Senders"</string>
|
<string name="task_sender_tips">Control the enable/disable of "Senders"</string>
|
||||||
<string name="task_alarm">Alarm</string>
|
<string name="task_alarm">Alarm Reminder</string>
|
||||||
<string name="task_alarm_tips">Alarm</string>
|
<string name="task_alarm_tips">Play music/vibrate phone to remind</string>
|
||||||
<string name="task_resend">Resend Message</string>
|
<string name="task_resend">Resend Message</string>
|
||||||
<string name="task_resend_tips">Resend forwarded records since N hours ago, 0=ALL</string>
|
<string name="task_resend_tips">Resend forwarded records since N hours ago, 0=ALL</string>
|
||||||
<string name="task_resend_desc" formatted="false">Resend forwarding records since %s hours ago for %s</string>
|
<string name="task_resend_desc" formatted="false">Resend forwarding records since %s hours ago for %s</string>
|
||||||
@ -1124,11 +1124,19 @@
|
|||||||
|
|
||||||
<string name="start_alarm">Start Alarm</string>
|
<string name="start_alarm">Start Alarm</string>
|
||||||
<string name="stop_alarm">Stop Alarm</string>
|
<string name="stop_alarm">Stop Alarm</string>
|
||||||
<string name="alarm_play_settings">Playback Settings</string>
|
<string name="alarm_play_settings">Play Music</string>
|
||||||
<string name="alarm_music">Specify Music</string>
|
<string name="alarm_music">Specify Music</string>
|
||||||
<string name="alarm_music_tips">Opt., download mp3/ogg/wav to the Download directory.</string>
|
<string name="alarm_music_tips">Opt., download mp3/ogg/wav to the Download directory.</string>
|
||||||
<string name="alarm_volume">Alarm Volume</string>
|
<string name="alarm_volume">Alarm Volume</string>
|
||||||
<string name="alarm_play_times">Play Times(0=Infinite)</string>
|
<string name="alarm_play_times">Play Times(0=Infinite)</string>
|
||||||
|
<string name="alarm_vibrate_settings">Vibrate Phone</string>
|
||||||
|
<string name="alarm_repeat_times">Repeat Times</string>
|
||||||
|
<string name="alarm_vibration_effect">Vibration Effect</string>
|
||||||
|
<string name="alarm_vibration_effect_tips">Syntax: =[strong], -[weak], _[no], 100ms each</string>
|
||||||
|
<string name="alarm_vibration_effect_1">Strong vibration</string>
|
||||||
|
<string name="alarm_vibration_effect_2">Weak vibration</string>
|
||||||
|
<string name="alarm_vibration_effect_3">No vibration</string>
|
||||||
|
<string name="alarm_settings_error">At least one of Play Music/Vibrate Phone must be enabled</string>
|
||||||
|
|
||||||
<string name="invalid_tag" formatted="false">%s tag is invalid: %s</string>
|
<string name="invalid_tag" formatted="false">%s tag is invalid: %s</string>
|
||||||
<string name="invalid_task_name">Please input task name.</string>
|
<string name="invalid_task_name">Please input task name.</string>
|
||||||
|
@ -974,8 +974,8 @@
|
|||||||
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
|
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
|
||||||
<string name="task_sender">启停通道</string>
|
<string name="task_sender">启停通道</string>
|
||||||
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
|
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
|
||||||
<string name="task_alarm">声音警报</string>
|
<string name="task_alarm">警报提醒</string>
|
||||||
<string name="task_alarm_tips">声音警报</string>
|
<string name="task_alarm_tips">播放音乐/振动手机提醒</string>
|
||||||
<string name="task_resend">重发消息</string>
|
<string name="task_resend">重发消息</string>
|
||||||
<string name="task_resend_tips">自动重发N小时以来的转发记录,0=全部</string>
|
<string name="task_resend_tips">自动重发N小时以来的转发记录,0=全部</string>
|
||||||
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
|
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
|
||||||
@ -1125,11 +1125,19 @@
|
|||||||
|
|
||||||
<string name="start_alarm">启动警报</string>
|
<string name="start_alarm">启动警报</string>
|
||||||
<string name="stop_alarm">停止警报</string>
|
<string name="stop_alarm">停止警报</string>
|
||||||
<string name="alarm_play_settings">播放设置</string>
|
<string name="alarm_play_settings">播放音乐</string>
|
||||||
<string name="alarm_music">指定音乐</string>
|
<string name="alarm_music">指定音乐</string>
|
||||||
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
|
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
|
||||||
<string name="alarm_volume">播放音量</string>
|
<string name="alarm_volume">播放音量</string>
|
||||||
<string name="alarm_play_times">播放次数(0=无限)</string>
|
<string name="alarm_play_times">播放次数(0=无限)</string>
|
||||||
|
<string name="alarm_vibrate_settings">振动手机</string>
|
||||||
|
<string name="alarm_repeat_times">重复次数</string>
|
||||||
|
<string name="alarm_vibration_effect">振动效果</string>
|
||||||
|
<string name="alarm_vibration_effect_tips">语法:=[强振动], -[弱震动], _[不振动], 每次100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_1">强振动 100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_2">弱振动 100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_3">不振动 100ms</string>
|
||||||
|
<string name="alarm_settings_error">播放音乐/振动手机必须至少开启一个</string>
|
||||||
|
|
||||||
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
|
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
|
||||||
<string name="invalid_task_name">请输入任务名称</string>
|
<string name="invalid_task_name">请输入任务名称</string>
|
||||||
|
@ -967,8 +967,8 @@
|
|||||||
<string name="task_rule_tips">控制【轉發規則】的啟用/禁用</string>
|
<string name="task_rule_tips">控制【轉發規則】的啟用/禁用</string>
|
||||||
<string name="task_sender">啟停通道</string>
|
<string name="task_sender">啟停通道</string>
|
||||||
<string name="task_sender_tips">控制【發送通道】的啟用/禁用</string>
|
<string name="task_sender_tips">控制【發送通道】的啟用/禁用</string>
|
||||||
<string name="task_alarm">聲音警報</string>
|
<string name="task_alarm">警報提醒</string>
|
||||||
<string name="task_alarm_tips">聲音警報</string>
|
<string name="task_alarm_tips">播放音樂/震動手機發出提醒</string>
|
||||||
<string name="task_resend">重發消息</string>
|
<string name="task_resend">重發消息</string>
|
||||||
<string name="task_resend_tips">自動重發N小時以來的轉發記錄,0=全部</string>
|
<string name="task_resend_tips">自動重發N小時以來的轉發記錄,0=全部</string>
|
||||||
<string name="task_resend_desc" formatted="false">自動重發%s小時以來%s的轉發記錄</string>
|
<string name="task_resend_desc" formatted="false">自動重發%s小時以來%s的轉發記錄</string>
|
||||||
@ -1118,11 +1118,19 @@
|
|||||||
|
|
||||||
<string name="start_alarm">啟動警報</string>
|
<string name="start_alarm">啟動警報</string>
|
||||||
<string name="stop_alarm">停止警報</string>
|
<string name="stop_alarm">停止警報</string>
|
||||||
<string name="alarm_play_settings">播放設置</string>
|
<string name="alarm_play_settings">播放音樂</string>
|
||||||
<string name="alarm_music">指定音樂</string>
|
<string name="alarm_music">指定音樂</string>
|
||||||
<string name="alarm_music_tips">可選,下載 mp3/ogg/wav 到 Download 目錄</string>
|
<string name="alarm_music_tips">可選,下載 mp3/ogg/wav 到 Download 目錄</string>
|
||||||
<string name="alarm_volume">播放音量</string>
|
<string name="alarm_volume">播放音量</string>
|
||||||
<string name="alarm_play_times">播放次數(0=無限)</string>
|
<string name="alarm_play_times">播放次數(0=無限)</string>
|
||||||
|
<string name="alarm_vibrate_settings">振動手機</string>
|
||||||
|
<string name="alarm_repeat_times">重複次數</string>
|
||||||
|
<string name="alarm_vibration_effect">振動效果</string>
|
||||||
|
<string name="alarm_vibration_effect_tips">語法:=[強振動], -[弱振動], _[不振動], 每次100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_1">強振動 100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_2">弱振動 100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_3">不振動 100ms</string>
|
||||||
|
<string name="alarm_settings_error">播放音樂/振動手機必須至少開啟一個</string>
|
||||||
|
|
||||||
<string name="invalid_tag" formatted="false">%s 標籤無效:%s</string>
|
<string name="invalid_tag" formatted="false">%s 標籤無效:%s</string>
|
||||||
<string name="invalid_task_name">請輸入任務名稱</string>
|
<string name="invalid_task_name">請輸入任務名稱</string>
|
||||||
|
@ -1001,8 +1001,8 @@
|
|||||||
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
|
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
|
||||||
<string name="task_sender">启停通道</string>
|
<string name="task_sender">启停通道</string>
|
||||||
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
|
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
|
||||||
<string name="task_alarm">声音警报</string>
|
<string name="task_alarm">警报提醒</string>
|
||||||
<string name="task_alarm_tips">播放音乐提醒</string>
|
<string name="task_alarm_tips">播放音乐/振动手机提醒</string>
|
||||||
<string name="task_resend">重发消息</string>
|
<string name="task_resend">重发消息</string>
|
||||||
<string name="task_resend_tips">自动重发N小时以来的转发记录,0=全部</string>
|
<string name="task_resend_tips">自动重发N小时以来的转发记录,0=全部</string>
|
||||||
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
|
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
|
||||||
@ -1152,11 +1152,19 @@
|
|||||||
|
|
||||||
<string name="start_alarm">启动警报</string>
|
<string name="start_alarm">启动警报</string>
|
||||||
<string name="stop_alarm">停止警报</string>
|
<string name="stop_alarm">停止警报</string>
|
||||||
<string name="alarm_play_settings">播放设置</string>
|
<string name="alarm_play_settings">播放音乐</string>
|
||||||
<string name="alarm_music">指定音乐</string>
|
<string name="alarm_music">指定音乐</string>
|
||||||
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
|
<string name="alarm_music_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
|
||||||
<string name="alarm_volume">播放音量</string>
|
<string name="alarm_volume">播放音量</string>
|
||||||
<string name="alarm_play_times">播放次数(0=无限)</string>
|
<string name="alarm_play_times">播放次数(0=无限)</string>
|
||||||
|
<string name="alarm_vibrate_settings">振动手机</string>
|
||||||
|
<string name="alarm_repeat_times">重复次数</string>
|
||||||
|
<string name="alarm_vibration_effect">振动效果</string>
|
||||||
|
<string name="alarm_vibration_effect_tips">语法:=[强振动], -[弱震动], _[不振动], 每次100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_1">强振动 100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_2">弱振动 100ms</string>
|
||||||
|
<string name="alarm_vibration_effect_3">不振动 100ms</string>
|
||||||
|
<string name="alarm_settings_error">播放音乐/振动手机必须至少开启一个</string>
|
||||||
|
|
||||||
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
|
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
|
||||||
<string name="invalid_task_name">请输入任务名称</string>
|
<string name="invalid_task_name">请输入任务名称</string>
|
||||||
|
@ -13,7 +13,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|||||||
# org.gradle.parallel=true
|
# org.gradle.parallel=true
|
||||||
# 是否打包APK,打正式包时请设置为true,使用正式的签名
|
# 是否打包APK,打正式包时请设置为true,使用正式的签名
|
||||||
isNeedPackage=true
|
isNeedPackage=true
|
||||||
isNeedClean=true
|
isNeedClean=false
|
||||||
# 是否排除Frpc动态库,打正式包时请设置为true
|
# 是否排除Frpc动态库,打正式包时请设置为true
|
||||||
excludeFrpclib=true
|
excludeFrpclib=true
|
||||||
# 是否使用leakcanary检测内存泄漏,打正式包时请设置为false
|
# 是否使用leakcanary检测内存泄漏,打正式包时请设置为false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user