Initial commit - v1.1.9
This commit is contained in:
498
app/src/main/java/com/example/shiftalarm/AlarmActivity.kt
Normal file
498
app/src/main/java/com/example/shiftalarm/AlarmActivity.kt
Normal file
@@ -0,0 +1,498 @@
|
||||
package com.example.shiftalarm
|
||||
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioManager
|
||||
import android.media.MediaPlayer
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.PowerManager
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.example.shiftalarm.databinding.ActivityAlarmBinding
|
||||
import androidx.core.content.ContextCompat
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.math.abs
|
||||
|
||||
class AlarmActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityAlarmBinding
|
||||
private var mediaPlayer: MediaPlayer? = null
|
||||
private var vibrator: Vibrator? = null
|
||||
private var startX = 0f
|
||||
|
||||
// 5분 후 자동 스누즈
|
||||
private val autoStopHandler = Handler(Looper.getMainLooper())
|
||||
private val AUTO_STOP_DELAY = 5L * 60 * 1000
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// 중요: 잠금 화면 위 표시 설정을 가장 먼저 적용
|
||||
setupLockScreenFlags()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// ForegroundService가 실행 중이면 먼저 중지
|
||||
stopService(Intent(this, AlarmForegroundService::class.java))
|
||||
|
||||
// Service 중지 후 약간의 지연을 두어 AudioFocus가 완전히 해제되도록 함
|
||||
try {
|
||||
Thread.sleep(100)
|
||||
} catch (e: InterruptedException) {
|
||||
// 무시
|
||||
}
|
||||
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityAlarmBinding.inflate(layoutInflater)
|
||||
binding.root.background = ContextCompat.getDrawable(this, R.drawable.bg_alarm_gradient)
|
||||
setContentView(binding.root)
|
||||
|
||||
// 추가 윈도우 플래그 설정
|
||||
setupWindowFlags()
|
||||
|
||||
val shift = intent.getStringExtra("EXTRA_SHIFT") ?: "근무"
|
||||
binding.tvShiftType.text = if (shift == "SNOOZE") "다시 울림 알람" else "[$shift] 근무 알람"
|
||||
|
||||
val now = java.util.Calendar.getInstance()
|
||||
val amPm = if (now.get(java.util.Calendar.AM_PM) == java.util.Calendar.AM) "오전" else "오후"
|
||||
val hour = now.get(java.util.Calendar.HOUR)
|
||||
val hourText = if (hour == 0) 12 else hour
|
||||
val min = now.get(java.util.Calendar.MINUTE)
|
||||
binding.tvCurrentTime.text = String.format("%s %d:%02d", amPm, hourText, min)
|
||||
|
||||
val today = LocalDate.now()
|
||||
val dayOfWeek = today.dayOfWeek.getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.KOREAN)
|
||||
binding.tvDate.text = String.format("%d월 %d일 %s", today.monthValue, today.dayOfMonth, dayOfWeek)
|
||||
|
||||
// 알람 시작 (화면 상태와 무관하게 항상 실행)
|
||||
startAlarm()
|
||||
setupControls()
|
||||
|
||||
// 5분 후 자동 스누즈
|
||||
autoStopHandler.postDelayed({
|
||||
Toast.makeText(this, "알람이 자동으로 다시 울림 설정되었습니다.", Toast.LENGTH_LONG).show()
|
||||
snoozeAlarm()
|
||||
stopAlarm()
|
||||
finish()
|
||||
}, AUTO_STOP_DELAY)
|
||||
|
||||
// 키가드(잠금화면) 상태 변화 리스너 등록
|
||||
registerKeyguardListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* 잠금 화면 관련 플래그를 super.onCreate 이전에 설정
|
||||
*/
|
||||
private fun setupLockScreenFlags() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(true)
|
||||
setTurnScreenOn(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupWindowFlags() {
|
||||
// ========================================
|
||||
// 알람 화면이 패턴/지문보다 먼저 표시되도록 설정
|
||||
// ========================================
|
||||
// 중요: requestDismissKeyguard()를 호출하면 패턴/지문이 먼저 뜸
|
||||
// 알람 화면을 먼저 띄우려면 FLAG_SHOW_WHEN_LOCKED만 사용해야 함
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
// 1. 가장 먼저: 화면 켜기 + 잠금화면 위에 표시
|
||||
setShowWhenLocked(true)
|
||||
setTurnScreenOn(true)
|
||||
}
|
||||
|
||||
// 2. 화면 켜짐 유지
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
||||
// 3. 하위 호환성: Android 8.0 이하
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
|
||||
@Suppress("DEPRECATION")
|
||||
window.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
||||
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
)
|
||||
}
|
||||
|
||||
// 4. Android 14+ 추가 플래그
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
||||
}
|
||||
|
||||
// 5. Android 10+ 레이아웃
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
|
||||
}
|
||||
|
||||
// 6. 전체화면 모드 (모든 기기 공통)
|
||||
setFullscreenMode()
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체화면 모드 설정
|
||||
*/
|
||||
private fun isSamsungDevice(): Boolean {
|
||||
val manufacturer = Build.MANUFACTURER?.lowercase() ?: ""
|
||||
val brand = Build.BRAND?.lowercase() ?: ""
|
||||
return manufacturer.contains("samsung") || brand.contains("samsung")
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체화면 모드 설정
|
||||
*/
|
||||
private fun setFullscreenMode() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Android 11+ (API 30+): WindowInsetsController 사용
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
window.insetsController?.let { controller ->
|
||||
controller.hide(android.view.WindowInsets.Type.statusBars() or android.view.WindowInsets.Type.navigationBars())
|
||||
controller.systemBarsBehavior = android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
}
|
||||
} else {
|
||||
// Android 10 이하: systemUiVisibility 사용
|
||||
@Suppress("DEPRECATION")
|
||||
window.decorView.systemUiVisibility = (
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupControls() {
|
||||
binding.btnSnooze.setOnClickListener {
|
||||
handleSnooze()
|
||||
}
|
||||
|
||||
// Swipe-to-dismiss for Stop Button
|
||||
var startX = 0f
|
||||
val dismissBtn = binding.btnDismiss
|
||||
val maxSwipe = dpToPx(100f).toFloat()
|
||||
|
||||
dismissBtn.setOnTouchListener { v, event ->
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
startX = event.rawX
|
||||
v.animate().cancel()
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val dx = event.rawX - startX
|
||||
val clampedDx = if (dx > 0) dx.coerceAtMost(maxSwipe) else dx.coerceAtLeast(-maxSwipe)
|
||||
v.translationX = clampedDx
|
||||
|
||||
// Visual feedback: scale up when near trigger
|
||||
val ratio = abs(clampedDx) / maxSwipe
|
||||
v.scaleX = 1f + (ratio * 0.15f)
|
||||
v.scaleY = 1f + (ratio * 0.15f)
|
||||
v.alpha = 1f - (ratio * 0.3f)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
val dx = event.rawX - startX
|
||||
if (abs(dx) > maxSwipe * 0.8f) {
|
||||
// Trigger Dismiss
|
||||
(getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator)?.vibrate(50)
|
||||
Toast.makeText(this, "알람 해제 완료", Toast.LENGTH_SHORT).show()
|
||||
stopAlarm(); finish()
|
||||
} else {
|
||||
// Reset
|
||||
v.animate()
|
||||
.translationX(0f)
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.alpha(1f)
|
||||
.setDuration(300)
|
||||
.start()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
// Pulse logic with enhanced glow
|
||||
fun startPulse() {
|
||||
binding.pulseCircle.scaleX = 0.85f; binding.pulseCircle.scaleY = 0.85f; binding.pulseCircle.alpha = 0.5f
|
||||
binding.pulseCircle.animate()
|
||||
.scaleX(1.3f).scaleY(1.3f).alpha(1.0f)
|
||||
.setDuration(1500)
|
||||
.withEndAction {
|
||||
binding.pulseCircle.animate()
|
||||
.scaleX(0.85f).scaleY(0.85f).alpha(0.5f)
|
||||
.setDuration(1500)
|
||||
.withEndAction { if(!isFinishing) startPulse() }
|
||||
.start()
|
||||
}
|
||||
.start()
|
||||
}
|
||||
startPulse()
|
||||
}
|
||||
|
||||
private fun handleSnooze() {
|
||||
(getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator)?.vibrate(50)
|
||||
val snoozeRepeat = intent.getIntExtra("EXTRA_SNOOZE_REPEAT", 3)
|
||||
val text = if (snoozeRepeat == 99) "다시 울림 설정됨" else "다시 울림 (${snoozeRepeat}회 남음)"
|
||||
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
|
||||
snoozeAlarm(); stopAlarm(); finish()
|
||||
}
|
||||
|
||||
private fun dpToPx(dp: Float): Int {
|
||||
return (dp * resources.displayMetrics.density).toInt()
|
||||
}
|
||||
|
||||
private fun startAlarm() {
|
||||
if (mediaPlayer?.isPlaying == true) {
|
||||
Log.d("AlarmActivity", "MediaPlayer가 이미 실행 중")
|
||||
return
|
||||
}
|
||||
|
||||
val soundUriStr = intent.getStringExtra("EXTRA_SOUND")
|
||||
val alarmUri = if (!soundUriStr.isNullOrEmpty()) {
|
||||
Uri.parse(soundUriStr)
|
||||
} else {
|
||||
val prefs = getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
||||
val globalUriStr = prefs.getString("alarm_uri", null)
|
||||
if (globalUriStr != null) Uri.parse(globalUriStr)
|
||||
else android.provider.Settings.System.DEFAULT_ALARM_ALERT_URI
|
||||
}
|
||||
|
||||
// AudioAttributes 강화: 화면 켜진 상태에서도 알람음이 울리도록
|
||||
val audioAttrs = AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_ALARM)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) // 볼륨 강제 적용
|
||||
.build()
|
||||
|
||||
// AudioManager를 통해 알람 볼륨 설정
|
||||
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val originalVolume = audioManager.getStreamVolume(AudioManager.STREAM_ALARM)
|
||||
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM)
|
||||
|
||||
// 알람 볼륨을 최대로 설정 (사용자가 나중에 조정 가능)
|
||||
try {
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_ALARM, maxVolume, 0)
|
||||
} catch (e: Exception) {
|
||||
Log.w("AlarmActivity", "알람 볼륨 설정 실패", e)
|
||||
}
|
||||
|
||||
var mediaPlayerStarted = false
|
||||
|
||||
try {
|
||||
mediaPlayer?.release()
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setAudioAttributes(audioAttrs)
|
||||
setDataSource(this@AlarmActivity, alarmUri!!)
|
||||
isLooping = true
|
||||
setVolume(1.0f, 1.0f) // 최대 볼륨
|
||||
prepare()
|
||||
start()
|
||||
}
|
||||
mediaPlayerStarted = true
|
||||
Log.d("AlarmActivity", "MediaPlayer 시작 성공 (사용자 지정음)")
|
||||
} catch (e: Exception) {
|
||||
Log.e("AlarmActivity", "MediaPlayer 시작 실패 (사용자 지정음), fallback 시도", e)
|
||||
|
||||
// Fallback 1: 시스템 기본 알람음
|
||||
try {
|
||||
val fallback = android.provider.Settings.System.DEFAULT_ALARM_ALERT_URI
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setAudioAttributes(audioAttrs)
|
||||
setDataSource(this@AlarmActivity, fallback)
|
||||
isLooping = true
|
||||
setVolume(1.0f, 1.0f)
|
||||
prepare()
|
||||
start()
|
||||
}
|
||||
mediaPlayerStarted = true
|
||||
Log.d("AlarmActivity", "MediaPlayer 시작 성공 (Fallback 1: 시스템 기본)")
|
||||
} catch (e2: Exception) {
|
||||
Log.e("AlarmActivity", "Fallback 1 실패", e2)
|
||||
|
||||
// Fallback 2: RingtoneManager에서 기본 알람 가져오기
|
||||
try {
|
||||
val ringtoneUri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_ALARM)
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setAudioAttributes(audioAttrs)
|
||||
setDataSource(this@AlarmActivity, ringtoneUri)
|
||||
isLooping = true
|
||||
setVolume(1.0f, 1.0f)
|
||||
prepare()
|
||||
start()
|
||||
}
|
||||
mediaPlayerStarted = true
|
||||
Log.d("AlarmActivity", "MediaPlayer 시작 성공 (Fallback 2: RingtoneManager)")
|
||||
} catch (e3: Exception) {
|
||||
Log.e("AlarmActivity", "모든 MediaPlayer 시작 실패", e3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 진동 시작 (알람음과 독립적으로 - 알람음 실패필도 진동은 울림)
|
||||
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val vibrationEffect = VibrationEffect.createWaveform(longArrayOf(0, 1000, 500, 1000), 0)
|
||||
vibrator?.vibrate(vibrationEffect)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
vibrator?.vibrate(longArrayOf(0, 1000, 500, 1000), 0)
|
||||
}
|
||||
Log.d("AlarmActivity", "진동 시작 성공")
|
||||
} catch (e: Exception) {
|
||||
Log.e("AlarmActivity", "진동 시작 실패", e)
|
||||
}
|
||||
|
||||
// 알람음 시작 실패 시 토스트 메시지
|
||||
if (!mediaPlayerStarted) {
|
||||
Toast.makeText(this, "알람음 재생에 실패했습니다. 진동으로 알려드립니다.", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun snoozeAlarm() {
|
||||
val snoozeMin = intent.getIntExtra("EXTRA_SNOOZE", 5)
|
||||
val snoozeRepeat = intent.getIntExtra("EXTRA_SNOOZE_REPEAT", 3)
|
||||
val soundUriStr = intent.getStringExtra("EXTRA_SOUND")
|
||||
|
||||
if (snoozeRepeat > 0) {
|
||||
val nextRepeat = if (snoozeRepeat == 99) 99 else snoozeRepeat - 1
|
||||
scheduleSnooze(this, snoozeMin, soundUriStr, nextRepeat)
|
||||
} else {
|
||||
Toast.makeText(this, "다시 울림 횟수를 모두 소모하여 알람을 종료합니다.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopAlarm() {
|
||||
stopService(Intent(this, AlarmForegroundService::class.java))
|
||||
|
||||
try {
|
||||
mediaPlayer?.let {
|
||||
if (it.isPlaying) it.stop()
|
||||
it.release()
|
||||
}
|
||||
} catch (e: Exception) {}
|
||||
mediaPlayer = null
|
||||
|
||||
try {
|
||||
vibrator?.cancel()
|
||||
} catch (e: Exception) {}
|
||||
vibrator = null
|
||||
|
||||
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager
|
||||
nm.cancel(1)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
setIntent(intent)
|
||||
val shift = intent.getStringExtra("EXTRA_SHIFT") ?: "근무"
|
||||
binding.tvShiftType.text = if (shift == "SNOOZE") "다시 울림 알람" else "[$shift] 근무 알람"
|
||||
|
||||
stopAlarm()
|
||||
startAlarm()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
// 화면 켜짐 및 잠금 화면 위 표시 재적용
|
||||
setupWindowFlags()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
autoStopHandler.removeCallbacksAndMessages(null)
|
||||
stopAlarm()
|
||||
unregisterKeyguardListener()
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 키가드(잠금화면) 상태 감지 및 알람 해제 처리
|
||||
// ========================================
|
||||
private var keyguardManager: KeyguardManager? = null
|
||||
private var keyguardCallback: KeyguardManager.KeyguardDismissCallback? = null
|
||||
|
||||
/**
|
||||
* 키가드(잠금화면) 상태 변화를 감지하여 알람을 적절히 처리
|
||||
* 안드로이드 버전별로 다른 방식으로 처리
|
||||
*/
|
||||
private fun registerKeyguardListener() {
|
||||
keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as? KeyguardManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// Android 8.0+: KeyguardDismissCallback 사용
|
||||
keyguardCallback = object : KeyguardManager.KeyguardDismissCallback() {
|
||||
override fun onDismissError() {
|
||||
Log.e("AlarmActivity", "Keyguard dismiss error")
|
||||
}
|
||||
|
||||
override fun onDismissSucceeded() {
|
||||
Log.d("AlarmActivity", "Keyguard dismissed successfully - 사용자가 패턴/지문으로 해제함")
|
||||
// 패턴/지문 해제 후 알람 계속 울리게 하려면 여기서 아무것도 하지 않음
|
||||
// 알람을 자동으로 멈추려면: stopAlarm(); finish()
|
||||
}
|
||||
|
||||
override fun onDismissCancelled() {
|
||||
Log.d("AlarmActivity", "Keyguard dismiss cancelled")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterKeyguardListener() {
|
||||
keyguardCallback = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 키가드(잠금화면)가 잠겨있는지 확인
|
||||
*/
|
||||
private fun isKeyguardLocked(): Boolean {
|
||||
return keyguardManager?.isKeyguardLocked ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 키가드(잠금화면)가 보안 잠금(패턴/PIN/지문)을 사용하는지 확인
|
||||
*/
|
||||
private fun isKeyguardSecure(): Boolean {
|
||||
return keyguardManager?.isKeyguardSecure ?: false
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
// 홈 버튼이나 다른 앱으로 전환 시 알람 계속 울리도록 함
|
||||
// 사용자가 의도적으로 알람을 해제하지 않았으므로
|
||||
Log.d("AlarmActivity", "onPause - 알람 계속 유지")
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
// 알람 화면이 백그라운드로 갔을 때
|
||||
// 잠금화면이 다시 잠기면 알람을 멈추지 않고 계속 유지
|
||||
Log.d("AlarmActivity", "onStop - 알람 계속 유지")
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
// 알람 화면이 다시 포커스를 받으면 전체화면 모드 재적용
|
||||
setFullscreenMode()
|
||||
Log.d("AlarmActivity", "Window focus regained")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user