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_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
|
@ -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
|
||||
|
@ -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<FragmentTasksActionAlarmBinding?>(), 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<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")
|
||||
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<FragmentTasksActionAlarmBinding?>(), 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<FragmentTasksActionAlarmBinding?>(), 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<FragmentTasksActionAlarmBinding?>(), View.OnC
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onGranted(permissions: List<String>, 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<FragmentTasksActionAlarmBinding?>(), 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<FragmentTasksActionAlarmBinding?>(), 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<String> {
|
||||
|
@ -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<AlarmSetting> { 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")
|
||||
}
|
||||
|
@ -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: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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:padding="0dp"
|
||||
app:xsb_max="100"
|
||||
app:xsb_min="1" />
|
||||
android:text="@string/alarm_play_settings"
|
||||
android:textStyle="bold"
|
||||
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
|
||||
android:id="@+id/layout_alarm_settings_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/alarm_play_times"
|
||||
android:textStyle="bold" />
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="?attr/xui_config_color_separator_light" />
|
||||
|
||||
<com.xuexiang.xui.widget.picker.XSeekBar
|
||||
android:id="@+id/xsb_loop_times"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:padding="0dp"
|
||||
app:xsb_max="30"
|
||||
app:xsb_min="0" />
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<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_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
|
||||
android:id="@+id/layout_vibrate_settings"
|
||||
style="@style/BarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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" />
|
||||
android:text="@string/alarm_vibrate_settings"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||
android:id="@+id/btn_file_picker"
|
||||
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||
android:id="@+id/sb_enable_vibrate"
|
||||
style="@style/SwitchButtonStyle"
|
||||
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_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" />
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/alarm_vibration_effect"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.ClearEditText
|
||||
android:id="@+id/et_vibration_effect"
|
||||
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>
|
||||
|
||||
|
@ -973,8 +973,8 @@
|
||||
<string name="task_rule_tips">Control the enable/disable of "Rules"</string>
|
||||
<string name="task_sender">Channels On/Off</string>
|
||||
<string name="task_sender_tips">Control the enable/disable of "Senders"</string>
|
||||
<string name="task_alarm">Alarm</string>
|
||||
<string name="task_alarm_tips">Alarm</string>
|
||||
<string name="task_alarm">Alarm Reminder</string>
|
||||
<string name="task_alarm_tips">Play music/vibrate phone to remind</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_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="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_tips">Opt., download mp3/ogg/wav to the Download directory.</string>
|
||||
<string name="alarm_volume">Alarm Volume</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_task_name">Please input task name.</string>
|
||||
|
@ -974,8 +974,8 @@
|
||||
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
|
||||
<string name="task_sender">启停通道</string>
|
||||
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
|
||||
<string name="task_alarm">声音警报</string>
|
||||
<string name="task_alarm_tips">声音警报</string>
|
||||
<string name="task_alarm">警报提醒</string>
|
||||
<string name="task_alarm_tips">播放音乐/振动手机提醒</string>
|
||||
<string name="task_resend">重发消息</string>
|
||||
<string name="task_resend_tips">自动重发N小时以来的转发记录,0=全部</string>
|
||||
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
|
||||
@ -1125,11 +1125,19 @@
|
||||
|
||||
<string name="start_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_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
|
||||
<string name="alarm_volume">播放音量</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_task_name">请输入任务名称</string>
|
||||
|
@ -967,8 +967,8 @@
|
||||
<string name="task_rule_tips">控制【轉發規則】的啟用/禁用</string>
|
||||
<string name="task_sender">啟停通道</string>
|
||||
<string name="task_sender_tips">控制【發送通道】的啟用/禁用</string>
|
||||
<string name="task_alarm">聲音警報</string>
|
||||
<string name="task_alarm_tips">聲音警報</string>
|
||||
<string name="task_alarm">警報提醒</string>
|
||||
<string name="task_alarm_tips">播放音樂/震動手機發出提醒</string>
|
||||
<string name="task_resend">重發消息</string>
|
||||
<string name="task_resend_tips">自動重發N小時以來的轉發記錄,0=全部</string>
|
||||
<string name="task_resend_desc" formatted="false">自動重發%s小時以來%s的轉發記錄</string>
|
||||
@ -1118,11 +1118,19 @@
|
||||
|
||||
<string name="start_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_tips">可選,下載 mp3/ogg/wav 到 Download 目錄</string>
|
||||
<string name="alarm_volume">播放音量</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_task_name">請輸入任務名稱</string>
|
||||
|
@ -1001,8 +1001,8 @@
|
||||
<string name="task_rule_tips">控制【转发规则】的启用/禁用</string>
|
||||
<string name="task_sender">启停通道</string>
|
||||
<string name="task_sender_tips">控制【发送通道】的启用/禁用</string>
|
||||
<string name="task_alarm">声音警报</string>
|
||||
<string name="task_alarm_tips">播放音乐提醒</string>
|
||||
<string name="task_alarm">警报提醒</string>
|
||||
<string name="task_alarm_tips">播放音乐/振动手机提醒</string>
|
||||
<string name="task_resend">重发消息</string>
|
||||
<string name="task_resend_tips">自动重发N小时以来的转发记录,0=全部</string>
|
||||
<string name="task_resend_desc" formatted="false">自动重发%s小时以来%s的转发记录</string>
|
||||
@ -1152,11 +1152,19 @@
|
||||
|
||||
<string name="start_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_tips">可选,下载 mp3/ogg/wav 到 Download 目录</string>
|
||||
<string name="alarm_volume">播放音量</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_task_name">请输入任务名称</string>
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user