Files
ShiftRing/app/src/main/java/com/example/shiftalarm/AlarmPermissionUtil.kt
T

293 lines
12 KiB
Kotlin

package com.example.shiftalarm
import android.Manifest
import android.app.Activity
import android.app.AlarmManager
import android.app.AlertDialog
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.coroutines.*
object AlarmPermissionUtil {
private var monitorJob: Job? = null
/**
* 시스템 설정 화면에서 사용자가 권한을 허용했을 때 뒤로가기를 누르지 않아도 앱으로 즉시 자동 복귀시킵니다.
*/
fun monitorAndAutoReturn(activity: Activity, isGrantedCheck: () -> Boolean) {
monitorJob?.cancel()
monitorJob = CoroutineScope(Dispatchers.Main).launch {
delay(1000) // 설정 화면 전환 시간 대기
var count = 0
while (count < 150 && isActive) { // 최대 60초간 500ms 주기로 체크
delay(500)
count++
if (isGrantedCheck()) {
try {
val returnIntent = Intent(activity, SettingsActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP
}
activity.startActivity(returnIntent)
} catch (e: Exception) {
e.printStackTrace()
}
break
}
}
}
}
fun cancelAutoReturn() {
monitorJob?.cancel()
monitorJob = null
}
/**
* 전체 권한 상태를 확인하고 필요한 경우 통합 안내 다이얼로그를 표시합니다.
*/
fun checkAndRequestAllPermissions(activity: ComponentActivity) {
val missingPermissions = mutableListOf<String>()
// 1. 알림 권한 (Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
missingPermissions.add("알림 표시 (알람 울림 확인)")
}
}
// 2. 정확한 알람 권한 (Android 12+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val am = activity.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (!am.canScheduleExactAlarms()) {
missingPermissions.add("정확한 알람 (정시에 울림 보장)")
}
}
// 3. 배터리 최적화 제외 (Android 6+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = activity.getSystemService(Context.POWER_SERVICE) as PowerManager
if (!pm.isIgnoringBatteryOptimizations(activity.packageName)) {
missingPermissions.add("배터리 최적화 제외 (절전 모드 무시)")
}
}
// 4. 전체화면 알림 권한 (Android 14+)
if (Build.VERSION.SDK_INT >= 34) {
val nm = activity.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (!nm.canUseFullScreenIntent()) {
missingPermissions.add("전체화면 알림 (잠금 화면에서 즉시 표시)")
}
}
if (missingPermissions.isNotEmpty()) {
showIntegratedPermissionDialog(activity, missingPermissions)
}
}
private fun showIntegratedPermissionDialog(activity: ComponentActivity, missing: List<String>) {
if (activity.isFinishing || activity.isDestroyed) return
val message = StringBuilder("안정적인 알람 작동을 위해 아래 권한들이 필요합니다:\n\n")
missing.forEach { message.append("- $it\n") }
message.append("\n[확인]을 누르면 설정 화면으로 순차적으로 안내합니다.")
try {
AlertDialog.Builder(activity)
.setTitle("권한 설정 안내")
.setMessage(message.toString())
.setPositiveButton("확인") { _, _ ->
startPermissionFlow(activity)
}
.setNegativeButton("나중에", null)
.setCancelable(false)
.show()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun startPermissionFlow(activity: ComponentActivity) {
// 순차적으로 가장 중요한 것부터 요청
// 1. 알림 권한 (시스템 팝업)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 101)
return // 알림 권한 결과 콜백 이후 다음으로 넘어가도록 유도 (혹은 그냥 연달아 띄움)
}
}
// 2. 배터리 최적화 (시스템 팝업)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = activity.getSystemService(Context.POWER_SERVICE) as PowerManager
if (!pm.isIgnoringBatteryOptimizations(activity.packageName)) {
requestBatteryOptimization(activity)
return
}
}
// 3. 정확한 알람 (설정 화면)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val am = activity.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (!am.canScheduleExactAlarms()) {
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
data = Uri.parse("package:${activity.packageName}")
}
activity.startActivity(intent)
return
}
}
// 4. 전체화면 알림 (설정 화면)
if (Build.VERSION.SDK_INT >= 34) {
val nm = activity.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (!nm.canUseFullScreenIntent()) {
try {
val intent = Intent("android.settings.MANAGE_APP_USE_FULL_SCREEN_INTENT").apply {
data = Uri.parse("package:${activity.packageName}")
}
activity.startActivity(intent)
} catch (e: Exception) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:${activity.packageName}")
}
activity.startActivity(intent)
}
}
}
}
fun requestBatteryOptimization(context: Context) {
try {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:${context.packageName}")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
} catch (e: Exception) {
try {
val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
} catch (e2: Exception) {
try {
// Samsung Galaxy Device Care Battery Activity
val intent = Intent().apply {
setClassName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
} catch (e3: Exception) {
try {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:${context.packageName}")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
} catch (e4: Exception) {
Toast.makeText(context, "배터리 설정 화면을 열 수 없습니다.", Toast.LENGTH_SHORT).show()
}
}
}
}
}
fun requestOverlayPermission(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
data = Uri.parse("package:${context.packageName}")
}
if (context !is Activity) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}
}
fun requestFullScreenIntentPermission(context: Context) {
if (Build.VERSION.SDK_INT >= 34) {
try {
val intent = Intent("android.settings.MANAGE_APP_USE_FULL_SCREEN_INTENT").apply {
data = Uri.parse("package:${context.packageName}")
}
if (context !is Activity) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
} catch (e: Exception) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:${context.packageName}")
}
if (context !is Activity) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}
}
}
fun isAllPermissionsGranted(context: Context): Boolean {
var allGranted = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
allGranted = allGranted && ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
allGranted = allGranted && am.canScheduleExactAlarms()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
allGranted = allGranted && pm.isIgnoringBatteryOptimizations(context.packageName)
}
if (Build.VERSION.SDK_INT >= 34) {
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
allGranted = allGranted && nm.canUseFullScreenIntent()
}
return allGranted
}
fun getBatteryOptimizationStatus(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
return pm.isIgnoringBatteryOptimizations(context.packageName)
}
return true
}
fun getExactAlarmStatus(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
return am.canScheduleExactAlarms()
}
return true
}
fun getOverlayStatus(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Settings.canDrawOverlays(context)
}
return true
}
fun getFullScreenIntentStatus(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= 34) {
val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
return nm.canUseFullScreenIntent()
}
return true
}
}