Initial commit - v1.1.9
This commit is contained in:
@@ -0,0 +1,574 @@
|
||||
package com.example.shiftalarm
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.TimePicker
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.materialswitch.MaterialSwitch
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.example.shiftalarm.databinding.FragmentSettingsAlarmBinding
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.time.LocalDate
|
||||
|
||||
class FragmentSettingsAlarm : Fragment(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private var _binding: FragmentSettingsAlarmBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val PREFS_NAME = "ShiftAlarmPrefs"
|
||||
private lateinit var repository: ShiftRepository
|
||||
private var customAlarms: MutableList<CustomAlarm> = mutableListOf()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentSettingsAlarmBinding.inflate(inflater, container, false)
|
||||
repository = ShiftRepository(requireContext())
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val prefs = requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.registerOnSharedPreferenceChangeListener(this)
|
||||
|
||||
setupListeners()
|
||||
loadSettings()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
refreshAlarmList()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
val prefs = requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.unregisterOnSharedPreferenceChangeListener(this)
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == "master_alarm_enabled") {
|
||||
sharedPreferences?.let {
|
||||
updateMasterToggleUI(ShiftAlarmDefaults.isMasterAlarmEnabled(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
val prefs = requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
|
||||
// Master Toggle Button State
|
||||
updateMasterToggleUI(ShiftAlarmDefaults.isMasterAlarmEnabled(prefs))
|
||||
|
||||
// Migrate and Refresh
|
||||
lifecycleScope.launch {
|
||||
migrateFromPrefsIfNecessary()
|
||||
refreshAlarmList()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun migrateFromPrefsIfNecessary() {
|
||||
val prefs = requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val legacyJson = prefs.getString("custom_alarms", null)
|
||||
if (legacyJson != null) {
|
||||
try {
|
||||
val arr = JSONArray(legacyJson)
|
||||
for (i in 0 until arr.length()) {
|
||||
val obj = arr.getJSONObject(i)
|
||||
val alarm = CustomAlarm(
|
||||
time = obj.getString("time"),
|
||||
shiftType = obj.getString("shiftType"),
|
||||
isEnabled = obj.optBoolean("enabled", true),
|
||||
soundUri = obj.optString("soundUri", null),
|
||||
snoozeInterval = obj.optInt("snoozeInterval", 5),
|
||||
snoozeRepeat = obj.optInt("snoozeRepeat", 3)
|
||||
)
|
||||
repository.addCustomAlarm(alarm)
|
||||
}
|
||||
// Clear legacy data
|
||||
prefs.edit().remove("custom_alarms").apply()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshAlarmList() {
|
||||
lifecycleScope.launch {
|
||||
customAlarms = repository.getAllCustomAlarms().toMutableList()
|
||||
refreshUI()
|
||||
}
|
||||
}
|
||||
|
||||
private val soundTitleCache = mutableMapOf<String?, String>()
|
||||
|
||||
private fun updateMasterToggleUI(isEnabled: Boolean) {
|
||||
if (isEnabled) {
|
||||
binding.tvMasterStatus.text = "전체 알람 켜짐"
|
||||
binding.tvMasterStatus.setTextColor(ContextCompat.getColor(requireContext(), R.color.primary))
|
||||
binding.tvMasterStatus.backgroundTintList = android.content.res.ColorStateList.valueOf(Color.parseColor("#E3F2FD"))
|
||||
} else {
|
||||
binding.tvMasterStatus.text = "전체 알람 꺼짐"
|
||||
binding.tvMasterStatus.setTextColor(ContextCompat.getColor(requireContext(), R.color.shift_red))
|
||||
binding.tvMasterStatus.backgroundTintList = android.content.res.ColorStateList.valueOf(Color.parseColor("#FFEBEE"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
val prefs = requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
|
||||
binding.tvMasterStatus.setOnClickListener {
|
||||
val isEnabled = !ShiftAlarmDefaults.isMasterAlarmEnabled(prefs)
|
||||
prefs.edit().putBoolean("master_alarm_enabled", isEnabled).apply()
|
||||
updateMasterToggleUI(isEnabled)
|
||||
|
||||
val message = if (isEnabled) "전체 알람이 켜졌습니다." else "전체 알람이 꺼졌습니다."
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Resync immediately
|
||||
lifecycleScope.launch { syncAllAlarms(requireContext()) }
|
||||
}
|
||||
|
||||
binding.btnAddCustomAlarm.setOnClickListener {
|
||||
showEditDialog(
|
||||
title = "새 알람 추가",
|
||||
currentTime = "07:00",
|
||||
shiftType = "주간",
|
||||
existingAlarm = null,
|
||||
isNew = true
|
||||
)
|
||||
}
|
||||
|
||||
binding.btnTestAlarm.setOnClickListener {
|
||||
scheduleTestAlarm(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshUI() {
|
||||
val container = binding.alarmListContainer
|
||||
container.removeAllViews()
|
||||
|
||||
for (alarm in customAlarms) {
|
||||
val item = createAlarmRow(alarm.shiftType, alarm.time, alarm.isEnabled, isCustom = true, snoozeMin = alarm.snoozeInterval, snoozeRepeat = alarm.snoozeRepeat, soundUri = alarm.soundUri) { isToggle, isLongOrShort ->
|
||||
if (isToggle) {
|
||||
// AlarmSyncManager를 사용하여 토글 동기화
|
||||
lifecycleScope.launch {
|
||||
val enable = !alarm.isEnabled
|
||||
val result = AlarmSyncManager.toggleAlarm(requireContext(), alarm, enable)
|
||||
if (result.isSuccess) {
|
||||
Log.d("ShiftAlarm", "알람 토글 동기화 성공: ID=${alarm.id}, enabled=$enable")
|
||||
} else {
|
||||
Log.e("ShiftAlarm", "알람 토글 동기화 실패", result.exceptionOrNull())
|
||||
Toast.makeText(requireContext(), "알람 상태 변경 중 오류가 발생했습니다.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
refreshAlarmList()
|
||||
}
|
||||
} else {
|
||||
showEditDialog("사용자 알람", alarm.time, alarm.shiftType, existingAlarm = alarm, isNew = false)
|
||||
}
|
||||
}
|
||||
container.addView(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAlarmRow(
|
||||
shiftName: String,
|
||||
time: String,
|
||||
isEnabled: Boolean,
|
||||
isCustom: Boolean,
|
||||
snoozeMin: Int,
|
||||
snoozeRepeat: Int,
|
||||
soundUri: String?,
|
||||
onAction: (isToggle: Boolean, isLongClick: Boolean) -> Unit
|
||||
): View {
|
||||
val view = layoutInflater.inflate(R.layout.item_alarm_unified, binding.alarmListContainer, false)
|
||||
view.isFocusable = true
|
||||
|
||||
val shiftIndicator = view.findViewById<TextView>(R.id.shiftIndicator)
|
||||
val tvTime = view.findViewById<TextView>(R.id.tvTime)
|
||||
val tvAmPm = view.findViewById<TextView>(R.id.tvAmPm)
|
||||
val tvSummary = view.findViewById<TextView>(R.id.tvSummary)
|
||||
val alarmSwitch = view.findViewById<MaterialSwitch>(R.id.alarmSwitch)
|
||||
val layoutAlarmSwitch = view.findViewById<View>(R.id.layoutAlarmSwitch)
|
||||
|
||||
val shortName = when(shiftName) {
|
||||
"주간" -> "주"
|
||||
"석간" -> "석"
|
||||
"야간" -> "야"
|
||||
"주간 맞교대" -> "주맞"
|
||||
"야간 맞교대" -> "야맞"
|
||||
"기타" -> "기타"
|
||||
else -> shiftName.take(1)
|
||||
}
|
||||
shiftIndicator.text = shortName
|
||||
|
||||
val colorRes = when(shiftName) {
|
||||
"주간" -> R.color.shift_lemon
|
||||
"석간" -> R.color.shift_seok
|
||||
"야간" -> R.color.shift_ya
|
||||
"주간 맞교대" -> R.color.shift_jumat
|
||||
"야간 맞교대" -> R.color.shift_yamat
|
||||
else -> R.color.shift_gray
|
||||
}
|
||||
|
||||
val context = requireContext()
|
||||
val color = ContextCompat.getColor(context, colorRes)
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.bg_shift_stroke_v4) as android.graphics.drawable.GradientDrawable
|
||||
drawable.mutate()
|
||||
drawable.setStroke(dpToPx(2.5f), color)
|
||||
shiftIndicator.background = drawable
|
||||
shiftIndicator.setTextColor(color)
|
||||
|
||||
try {
|
||||
val parts = time.split(":")
|
||||
val h24 = parts[0].toInt()
|
||||
val m = parts[1].toInt()
|
||||
val h12 = if (h24 % 12 == 0) 12 else h24 % 12
|
||||
tvTime.text = String.format("%02d:%02d", h12, m)
|
||||
tvAmPm.text = if (h24 < 12) "오전" else "오후"
|
||||
|
||||
if (!isEnabled) {
|
||||
tvTime.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
|
||||
tvAmPm.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
|
||||
tvSummary.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
|
||||
shiftIndicator.alpha = 0.4f
|
||||
} else {
|
||||
tvTime.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
|
||||
tvAmPm.setTextColor(ContextCompat.getColor(context, R.color.text_secondary))
|
||||
tvSummary.setTextColor(ContextCompat.getColor(context, R.color.primary))
|
||||
shiftIndicator.alpha = 1.0f
|
||||
}
|
||||
} catch (e: Exception) { tvTime.text = time }
|
||||
|
||||
val tvSoundNameView = view.findViewById<TextView>(R.id.tvSoundName)
|
||||
val soundName = getSoundTitle(context, soundUri)
|
||||
tvSummary.text = "${snoozeMin}분 간격, ${if(snoozeRepeat == 99) "계속" else snoozeRepeat.toString() + "회"}"
|
||||
tvSoundNameView.text = soundName
|
||||
|
||||
val rowContents = view.findViewById<View>(R.id.rowContents)
|
||||
rowContents.setOnClickListener { onAction(false, false) }
|
||||
rowContents.setOnLongClickListener { onAction(false, true); true }
|
||||
|
||||
alarmSwitch.isChecked = isEnabled
|
||||
layoutAlarmSwitch.setOnClickListener {
|
||||
// onAction will handle the data update and re-sync
|
||||
onAction(true, false)
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private var currentDialogSoundUri: String? = null
|
||||
private var tvSoundNameReference: android.widget.TextView? = null
|
||||
|
||||
/**
|
||||
* 새 알람 추가 시 기본음으로 시스템 알람음 설정
|
||||
* 무음 문제 해결을 위해 반드시 시스템 기본 알람음을 반환
|
||||
*/
|
||||
private fun getDefaultAlarmUri(context: Context): String {
|
||||
// 1. 시스템 기본 알람음 (가장 우선)
|
||||
val defaultUri = android.provider.Settings.System.DEFAULT_ALARM_ALERT_URI
|
||||
if (defaultUri != null) {
|
||||
Log.d("ShiftAlarm", "시스템 기본 알람음 URI: $defaultUri")
|
||||
return defaultUri.toString()
|
||||
}
|
||||
|
||||
// 2. RingtoneManager에서 알람 타입 기본값 가져오기
|
||||
val fallbackUri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_ALARM)
|
||||
if (fallbackUri != null) {
|
||||
Log.d("ShiftAlarm", "Fallback 알람음 URI: $fallbackUri")
|
||||
return fallbackUri.toString()
|
||||
}
|
||||
|
||||
// 3. 마지막 fallback: 알림음이라도 사용
|
||||
val notificationUri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_NOTIFICATION)
|
||||
if (notificationUri != null) {
|
||||
Log.w("ShiftAlarm", "알람음 없음, 알림음 사용: $notificationUri")
|
||||
return notificationUri.toString()
|
||||
}
|
||||
|
||||
// 4. 최후의 수단: 벨소리
|
||||
val ringtoneUri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_RINGTONE)
|
||||
if (ringtoneUri != null) {
|
||||
Log.w("ShiftAlarm", "알림음 없음, 벨소리 사용: $ringtoneUri")
|
||||
return ringtoneUri.toString()
|
||||
}
|
||||
|
||||
// 이 경우는 거의 없지만, 안전장치
|
||||
Log.e("ShiftAlarm", "어떤 기본 소리도 찾을 수 없음")
|
||||
return ""
|
||||
}
|
||||
|
||||
private fun showEditDialog(
|
||||
title: String, currentTime: String, shiftType: String, existingAlarm: CustomAlarm?, isNew: Boolean
|
||||
) {
|
||||
val dialogView = layoutInflater.inflate(R.layout.dialog_alarm_edit_spinner, null)
|
||||
val dialog = AlertDialog.Builder(requireContext(), android.R.style.Theme_DeviceDefault_Light_NoActionBar_Fullscreen).setView(dialogView).create()
|
||||
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
|
||||
val tvTitle = dialogView.findViewById<TextView>(R.id.dialogTitle)
|
||||
val timePicker = dialogView.findViewById<TimePicker>(R.id.timePicker)
|
||||
val tvSoundName = dialogView.findViewById<TextView>(R.id.tvSoundName)
|
||||
tvSoundNameReference = tvSoundName
|
||||
|
||||
val btnSelectSound = dialogView.findViewById<View>(R.id.btnSelectSound)
|
||||
val btnDelete = dialogView.findViewById<Button>(R.id.btnDelete)
|
||||
val btnCancel = dialogView.findViewById<View>(R.id.btnCancel)
|
||||
val btnSave = dialogView.findViewById<View>(R.id.btnSave)
|
||||
|
||||
// Initialize Values
|
||||
var selectedSnooze = existingAlarm?.snoozeInterval ?: 5
|
||||
var selectedRepeat = existingAlarm?.snoozeRepeat ?: 3
|
||||
|
||||
// 새 알람 생성 시 기본적으로 시스템 알람음 설정 (무음 문제 해결)
|
||||
// 기존 알람 수정 시에도 soundUri가 비어있거나 null이면 기본값으로 설정
|
||||
val existingUri = existingAlarm?.soundUri
|
||||
val isExistingUriEmpty = existingUri.isNullOrEmpty() || existingUri == "null"
|
||||
|
||||
currentDialogSoundUri = if (isNew || isExistingUriEmpty) {
|
||||
// 새 알람 또는 기존 알람의 소리가 설정되지 않은 경우: 반드시 기본 알람음으로 설정
|
||||
val defaultUri = getDefaultAlarmUri(requireContext())
|
||||
Log.d("ShiftAlarm", "기본 알람음 설정: $defaultUri (isNew=$isNew, isExistingUriEmpty=$isExistingUriEmpty)")
|
||||
defaultUri
|
||||
} else {
|
||||
// 기존 알람 수정: 기존 값 유지
|
||||
existingUri
|
||||
}
|
||||
|
||||
// soundUri가 비어있는 경우 최종 안전장치
|
||||
if (currentDialogSoundUri.isNullOrEmpty()) {
|
||||
currentDialogSoundUri = getDefaultAlarmUri(requireContext())
|
||||
Log.w("ShiftAlarm", "soundUri가 비어있어 기본값으로 재설정: $currentDialogSoundUri")
|
||||
}
|
||||
|
||||
Log.d("ShiftAlarm", "알람 ${if (isNew) "생성" else "수정"} - 최종 soundUri: $currentDialogSoundUri")
|
||||
|
||||
fun updateSoundName(uriStr: String?) {
|
||||
if (uriStr.isNullOrEmpty() || uriStr == "null") {
|
||||
tvSoundName.text = "기본 알람음"
|
||||
} else {
|
||||
try {
|
||||
val uri = android.net.Uri.parse(uriStr)
|
||||
val ringtone = android.media.RingtoneManager.getRingtone(requireContext(), uri)
|
||||
val title = ringtone?.getTitle(requireContext()) ?: "알람음"
|
||||
tvSoundName.text = title
|
||||
} catch (e: Exception) {
|
||||
tvSoundName.text = "알람음"
|
||||
}
|
||||
}
|
||||
}
|
||||
updateSoundName(currentDialogSoundUri)
|
||||
|
||||
// Snooze Interval Buttons
|
||||
val snoozeButtons = listOf(
|
||||
dialogView.findViewById<TextView>(R.id.snooze5),
|
||||
dialogView.findViewById<TextView>(R.id.snooze10),
|
||||
dialogView.findViewById<TextView>(R.id.snooze15),
|
||||
dialogView.findViewById<TextView>(R.id.snooze30)
|
||||
)
|
||||
val snoozeValues = listOf(5, 10, 15, 30)
|
||||
fun updateSnoozeUI() {
|
||||
snoozeButtons.forEachIndexed { i, btn ->
|
||||
val isSelected = snoozeValues[i] == selectedSnooze
|
||||
btn.setBackgroundResource(if (isSelected) R.drawable.bg_pill_rect_selected else R.drawable.bg_pill_rect_unselected)
|
||||
btn.setTextColor(if (isSelected) ContextCompat.getColor(requireContext(), R.color.white) else ContextCompat.getColor(requireContext(), R.color.text_secondary))
|
||||
}
|
||||
}
|
||||
updateSnoozeUI()
|
||||
snoozeButtons.forEachIndexed { i, btn -> btn.setOnClickListener { selectedSnooze = snoozeValues[i]; updateSnoozeUI() } }
|
||||
|
||||
// Repeat Count Buttons
|
||||
val repeatButtons = listOf(
|
||||
dialogView.findViewById<TextView>(R.id.repeat3),
|
||||
dialogView.findViewById<TextView>(R.id.repeat5),
|
||||
dialogView.findViewById<TextView>(R.id.repeatForever)
|
||||
)
|
||||
val repeatValues = listOf(3, 5, 99)
|
||||
fun updateRepeatUI() {
|
||||
repeatButtons.forEachIndexed { i, btn ->
|
||||
val isSelected = repeatValues[i] == selectedRepeat
|
||||
btn.setBackgroundResource(if (isSelected) R.drawable.bg_pill_rect_selected else R.drawable.bg_pill_rect_unselected)
|
||||
btn.setTextColor(if (isSelected) ContextCompat.getColor(requireContext(), R.color.white) else ContextCompat.getColor(requireContext(), R.color.text_secondary))
|
||||
}
|
||||
}
|
||||
updateRepeatUI()
|
||||
repeatButtons.forEachIndexed { i, btn -> btn.setOnClickListener { selectedRepeat = repeatValues[i]; updateRepeatUI() } }
|
||||
|
||||
val cardShift = dialogView.findViewById<View>(R.id.cardShiftSelector)
|
||||
var currentShift = shiftType
|
||||
cardShift.visibility = View.VISIBLE
|
||||
val shiftBtns = listOf(
|
||||
dialogView.findViewById<TextView>(R.id.btnShiftJu),
|
||||
dialogView.findViewById<TextView>(R.id.btnShiftSeok),
|
||||
dialogView.findViewById<TextView>(R.id.btnShiftYa),
|
||||
dialogView.findViewById<TextView>(R.id.btnShiftYaMat),
|
||||
dialogView.findViewById<TextView>(R.id.btnShiftEtc)
|
||||
)
|
||||
val shiftTypes = listOf("주간", "석간", "야간", "야간 맞교대", "기타")
|
||||
fun updateShiftUI() {
|
||||
shiftBtns.forEachIndexed { i, btn ->
|
||||
val isSelected = shiftTypes[i] == currentShift
|
||||
val colorRes = when(shiftTypes[i]) {
|
||||
"주간" -> R.color.shift_lemon; "석간" -> R.color.shift_seok; "야간" -> R.color.shift_ya
|
||||
"야간 맞교대" -> R.color.shift_yamat; else -> R.color.shift_gray
|
||||
}
|
||||
val color = ContextCompat.getColor(requireContext(), colorRes)
|
||||
if (isSelected) {
|
||||
val d = ContextCompat.getDrawable(requireContext(), R.drawable.bg_shift_circle_v4) as android.graphics.drawable.GradientDrawable
|
||||
d.mutate(); d.setColor(color); btn.background = d
|
||||
btn.setTextColor(if (shiftTypes[i] == "야간") ContextCompat.getColor(requireContext(), R.color.white) else ContextCompat.getColor(requireContext(), R.color.black))
|
||||
} else {
|
||||
val d = ContextCompat.getDrawable(requireContext(), R.drawable.bg_shift_stroke_v4) as android.graphics.drawable.GradientDrawable
|
||||
d.mutate(); d.setStroke(dpToPx(1.5f), color); btn.background = d; btn.setTextColor(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
updateShiftUI()
|
||||
shiftBtns.forEachIndexed { i, btn -> btn.setOnClickListener { currentShift = shiftTypes[i]; updateShiftUI() } }
|
||||
|
||||
tvTitle.text = if (isNew) "새 알람 추가" else "$shiftType 알람 수정"
|
||||
timePicker.setIs24HourView(false)
|
||||
val parts = currentTime.split(":")
|
||||
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||
timePicker.hour = parts[0].toInt(); timePicker.minute = parts[1].toInt()
|
||||
} else {
|
||||
timePicker.currentHour = parts[0].toInt(); timePicker.currentMinute = parts[1].toInt()
|
||||
}
|
||||
|
||||
btnSelectSound.setOnClickListener {
|
||||
val intent = Intent(android.media.RingtoneManager.ACTION_RINGTONE_PICKER).apply {
|
||||
putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_TYPE, android.media.RingtoneManager.TYPE_ALARM)
|
||||
putExtra(android.media.RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, if (currentDialogSoundUri != null) android.net.Uri.parse(currentDialogSoundUri) else null as android.net.Uri?)
|
||||
}
|
||||
startActivityForResult(intent, 100)
|
||||
}
|
||||
|
||||
if (!isNew) {
|
||||
btnDelete.visibility = View.VISIBLE
|
||||
btnDelete.setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
existingAlarm?.let {
|
||||
// AlarmSyncManager를 사용하여 동기화된 삭제 수행
|
||||
// DB 삭제 전 AlarmManager 취소가 보장됨
|
||||
val result = AlarmSyncManager.deleteAlarm(requireContext(), it)
|
||||
if (result.isSuccess) {
|
||||
Log.d("ShiftAlarm", "알람 삭제 동기화 성공: ID=${it.id}")
|
||||
} else {
|
||||
Log.e("ShiftAlarm", "알람 삭제 동기화 실패", result.exceptionOrNull())
|
||||
Toast.makeText(requireContext(), "알람 삭제 중 오류가 발생했습니다.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
refreshAlarmList()
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
btnCancel.setOnClickListener { dialog.dismiss() }
|
||||
btnSave.setOnClickListener {
|
||||
val h = if (android.os.Build.VERSION.SDK_INT >= 23) timePicker.hour else timePicker.currentHour
|
||||
val m = if (android.os.Build.VERSION.SDK_INT >= 23) timePicker.minute else timePicker.currentMinute
|
||||
val time = String.format("%02d:%02d", h, m)
|
||||
|
||||
lifecycleScope.launch {
|
||||
if (isNew) {
|
||||
val newAlarm = CustomAlarm(
|
||||
time = time,
|
||||
shiftType = currentShift,
|
||||
isEnabled = true,
|
||||
soundUri = currentDialogSoundUri,
|
||||
snoozeInterval = selectedSnooze,
|
||||
snoozeRepeat = selectedRepeat
|
||||
)
|
||||
// AlarmSyncManager를 사용하여 동기화된 추가 수행
|
||||
val result = AlarmSyncManager.addAlarm(requireContext(), newAlarm)
|
||||
if (result.isSuccess) {
|
||||
Log.d("ShiftAlarm", "새 알람 추가 동기화 성공")
|
||||
Toast.makeText(requireContext(), "알람이 추가되었습니다.", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Log.e("ShiftAlarm", "새 알람 추가 동기화 실패", result.exceptionOrNull())
|
||||
Toast.makeText(requireContext(), "알람 추가 중 오류가 발생했습니다.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
val updated = existingAlarm!!.copy(
|
||||
time = time,
|
||||
shiftType = currentShift,
|
||||
soundUri = currentDialogSoundUri,
|
||||
snoozeInterval = selectedSnooze,
|
||||
snoozeRepeat = selectedRepeat
|
||||
)
|
||||
// AlarmSyncManager를 사용하여 동기화된 수정 수행
|
||||
val result = AlarmSyncManager.updateAlarm(requireContext(), updated)
|
||||
if (result.isSuccess) {
|
||||
Log.d("ShiftAlarm", "알람 수정 동기화 성공: ID=${updated.id}")
|
||||
Toast.makeText(requireContext(), "알람이 수정되었습니다.", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Log.e("ShiftAlarm", "알람 수정 동기화 실패", result.exceptionOrNull())
|
||||
Toast.makeText(requireContext(), "알람 수정 중 오류가 발생했습니다.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
refreshAlarmList()
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
dialog.setOnDismissListener { tvSoundNameReference = null }
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun dpToPx(dp: Float): Int {
|
||||
return (dp * resources.displayMetrics.density).toInt()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: android.content.Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == androidx.appcompat.app.AppCompatActivity.RESULT_OK) {
|
||||
val uri = data?.getParcelableExtra<android.net.Uri>(android.media.RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
if (uri != null) {
|
||||
currentDialogSoundUri = uri.toString()
|
||||
try {
|
||||
val ringtone = android.media.RingtoneManager.getRingtone(requireContext(), uri)
|
||||
tvSoundNameReference?.text = ringtone.getTitle(requireContext())
|
||||
} catch(e: Exception) {
|
||||
tvSoundNameReference?.text = "사용자 지정음"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun getSoundTitle(context: Context, uriStr: String?): String {
|
||||
if (soundTitleCache.containsKey(uriStr)) return soundTitleCache[uriStr]!!
|
||||
|
||||
// uriStr이 null이거나 비어있거나 "null" 문자열인 경우 기본음으로 처리
|
||||
val title = if (uriStr.isNullOrEmpty() || uriStr == "null") {
|
||||
"기본 알람음"
|
||||
} else {
|
||||
try {
|
||||
val uri = android.net.Uri.parse(uriStr)
|
||||
val ringtone = android.media.RingtoneManager.getRingtone(context, uri)
|
||||
ringtone?.getTitle(context) ?: "알람음"
|
||||
} catch (e: Exception) {
|
||||
"알람음"
|
||||
}
|
||||
}
|
||||
soundTitleCache[uriStr] = title
|
||||
return title
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user