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) loadAppVersion() } 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() } } private fun loadAppVersion() { try { val packageInfo = requireContext().packageManager.getPackageInfo(requireContext().packageName, 0) val versionName = packageInfo.versionName binding.tvAppVersion.text = "버전 $versionName" } catch (e: Exception) { binding.tvAppVersion.text = "버전 1.4.0" } } override fun onDestroyView() { super.onDestroyView() _binding = null } }