Initial commit - v1.1.9
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
package com.example.shiftalarm
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.example.shiftalarm.databinding.FragmentSettingsAdditionalBinding
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
class FragmentSettingsAdditional : Fragment() {
|
||||
private var _binding: FragmentSettingsAdditionalBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val PREFS_NAME = "ShiftAlarmPrefs"
|
||||
private var isUserInteraction = false
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FragmentSettingsAdditionalBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
loadSettings()
|
||||
setupListeners()
|
||||
}
|
||||
|
||||
private val backupLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
||||
uri?.let {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val db = AppDatabase.getDatabase(requireContext())
|
||||
BackupManager.backupData(requireContext(), it, db.shiftDao())
|
||||
Toast.makeText(requireContext(), "백업이 완료되었습니다.", Toast.LENGTH_SHORT).show()
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(requireContext(), "백업 실패: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val restoreLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||
uri?.let {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val db = AppDatabase.getDatabase(requireContext())
|
||||
BackupManager.restoreData(requireContext(), it, db.shiftDao())
|
||||
androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle("복구 완료")
|
||||
.setMessage("데이터 복구가 완료되었습니다. 변경사항을 적용하기 위해 앱을 재시작해야 합니다.")
|
||||
.setPositiveButton("앱 재시작") { _, _ ->
|
||||
val intent = requireContext().packageManager.getLaunchIntentForPackage(requireContext().packageName)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
requireActivity().finish()
|
||||
}
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
loadSettings()
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(requireContext(), "복구 실패: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
val prefs = requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
|
||||
// Theme Spinner
|
||||
val themeOptions = resources.getStringArray(R.array.theme_array)
|
||||
val themeAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, themeOptions)
|
||||
themeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
binding.themeSpinner.adapter = themeAdapter
|
||||
|
||||
val themeMode = prefs.getInt("theme_mode", androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
val themeIndex = when(themeMode) {
|
||||
androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO -> 1
|
||||
androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES -> 2
|
||||
else -> 0
|
||||
}
|
||||
binding.themeSpinner.setSelection(themeIndex)
|
||||
|
||||
// Tide Switch
|
||||
binding.switchTide.isChecked = prefs.getBoolean("show_tide", false)
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
val prefs = requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
|
||||
binding.themeSpinner.setOnTouchListener { _, _ ->
|
||||
isUserInteraction = true
|
||||
false
|
||||
}
|
||||
|
||||
binding.themeSpinner.onItemSelectedListener = object : android.widget.AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: android.widget.AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
if (!isUserInteraction) return
|
||||
|
||||
val themeMode = when(position) {
|
||||
1 -> androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
||||
2 -> androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
||||
else -> androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
|
||||
// Save and Apply
|
||||
val currentMode = prefs.getInt("theme_mode", -1)
|
||||
if (currentMode != themeMode) {
|
||||
prefs.edit().putInt("theme_mode", themeMode).apply()
|
||||
|
||||
// Critical Guard: Only apply if it actually changes the global state
|
||||
if (androidx.appcompat.app.AppCompatDelegate.getDefaultNightMode() != themeMode) {
|
||||
androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode(themeMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onNothingSelected(parent: android.widget.AdapterView<*>?) {}
|
||||
}
|
||||
|
||||
// Tide Switch Listener (Fixed: properly saving now)
|
||||
binding.switchTide.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.edit().putBoolean("show_tide", isChecked).apply()
|
||||
}
|
||||
|
||||
// Backup/Restore buttons
|
||||
binding.btnBackup.setOnClickListener {
|
||||
val dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmm"))
|
||||
backupLauncher.launch("shiftring_backup_$dateStr.json")
|
||||
}
|
||||
|
||||
binding.btnRestore.setOnClickListener {
|
||||
restoreLauncher.launch(arrayOf("application/json"))
|
||||
}
|
||||
|
||||
binding.btnManual.setOnClickListener {
|
||||
startActivity(Intent(requireContext(), ManualActivity::class.java))
|
||||
}
|
||||
|
||||
binding.btnNotice.setOnClickListener {
|
||||
startActivity(Intent(requireContext(), NoticeActivity::class.java))
|
||||
}
|
||||
|
||||
binding.btnShareApp.setOnClickListener {
|
||||
lifecycleScope.launch(kotlinx.coroutines.Dispatchers.IO) {
|
||||
try {
|
||||
val context = requireContext()
|
||||
val pm = context.packageManager
|
||||
val appInfo = pm.getApplicationInfo(context.packageName, 0)
|
||||
val apkFile = java.io.File(appInfo.sourceDir)
|
||||
|
||||
val cachePath = java.io.File(context.cacheDir, "apks")
|
||||
cachePath.mkdirs()
|
||||
val newFile = java.io.File(cachePath, "ShiftRing_Installer.apk")
|
||||
|
||||
apkFile.copyTo(newFile, overwrite = true)
|
||||
|
||||
val contentUri = androidx.core.content.FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.provider",
|
||||
newFile
|
||||
)
|
||||
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.type = "application/vnd.android.package-archive"
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
|
||||
startActivity(Intent.createChooser(shareIntent, "앱 설치 파일 공유하기"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
|
||||
Toast.makeText(requireContext(), "공유 실패: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnResetOverrides.setOnClickListener {
|
||||
androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle("데이터 초기화")
|
||||
.setMessage("달력에서 개별적으로 바꾼 모든 근무와 알람 설정이 삭제됩니다. 계속하시겠습니까?")
|
||||
.setPositiveButton("초기화") { _, _ ->
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val db = AppDatabase.getDatabase(requireContext())
|
||||
val dao = db.shiftDao()
|
||||
dao.clearOverrides()
|
||||
|
||||
// Immediately re-sync all alarms
|
||||
syncAllAlarms(requireContext())
|
||||
|
||||
Toast.makeText(requireContext(), "모든 개별 설정이 삭제되고 알람이 재설정되었습니다.", Toast.LENGTH_SHORT).show()
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(requireContext(), "초기화 실패: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton("취소", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user