android 14 testing started.
parent
1f5c681e78
commit
d9c0ce5180
@ -0,0 +1,225 @@
|
||||
package com.example.hmg_qline.hmg_qline
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
* Utility class for scheduling app restart alarms.
|
||||
* Handles Android version-specific alarm scheduling with proper backward compatibility.
|
||||
*
|
||||
* Android Version Compatibility:
|
||||
* - Android 14+ (API 34): Uses USE_EXACT_ALARM or SCHEDULE_EXACT_ALARM with permission check
|
||||
* - Android 12-13 (API 31-33): Uses SCHEDULE_EXACT_ALARM with permission check
|
||||
* - Android 6-11 (API 23-30): Uses setExactAndAllowWhileIdle
|
||||
* - Android < 6 (API < 23): Uses setExact
|
||||
*/
|
||||
object AlarmScheduler {
|
||||
|
||||
private const val TAG = "AlarmScheduler"
|
||||
private const val RESTART_ALARM_REQUEST_CODE = 1001
|
||||
|
||||
/**
|
||||
* Schedule a daily restart alarm at the specified time.
|
||||
*
|
||||
* @param context Application context
|
||||
* @param hour Hour of day (0-23), default is 0 (midnight)
|
||||
* @param minute Minute (0-59), default is 15
|
||||
*/
|
||||
fun scheduleRestartAlarm(context: Context, hour: Int = 0, minute: Int = 15) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
|
||||
val intent = Intent(context, RestartAlarmReceiver::class.java).apply {
|
||||
action = RestartAlarmReceiver.ACTION_SCHEDULED_RESTART
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
RESTART_ALARM_REQUEST_CODE,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
// Calculate next alarm time
|
||||
val calendar = Calendar.getInstance().apply {
|
||||
set(Calendar.HOUR_OF_DAY, hour)
|
||||
set(Calendar.MINUTE, minute)
|
||||
set(Calendar.SECOND, 0)
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
|
||||
// If the time has already passed today, schedule for tomorrow
|
||||
if (timeInMillis <= System.currentTimeMillis()) {
|
||||
add(Calendar.DAY_OF_YEAR, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel any existing alarm first
|
||||
alarmManager.cancel(pendingIntent)
|
||||
|
||||
Log.d(TAG, "Scheduling restart alarm for: ${calendar.time}")
|
||||
Log.d(TAG, "Android SDK Version: ${Build.VERSION.SDK_INT}")
|
||||
|
||||
// Schedule based on Android version
|
||||
when {
|
||||
// Android 14+ (API 34+)
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> {
|
||||
scheduleForAndroid14Plus(context, alarmManager, pendingIntent, calendar.timeInMillis)
|
||||
}
|
||||
// Android 12-13 (API 31-33)
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
scheduleForAndroid12To13(context, alarmManager, pendingIntent, calendar.timeInMillis)
|
||||
}
|
||||
// Android 6-11 (API 23-30)
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
|
||||
alarmManager.setExactAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
calendar.timeInMillis,
|
||||
pendingIntent
|
||||
)
|
||||
Log.d(TAG, "Alarm scheduled using setExactAndAllowWhileIdle (API 23-30)")
|
||||
}
|
||||
// Android < 6 (API < 23)
|
||||
else -> {
|
||||
alarmManager.setExact(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
calendar.timeInMillis,
|
||||
pendingIntent
|
||||
)
|
||||
Log.d(TAG, "Alarm scheduled using setExact (API < 23)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule alarm for Android 14+ (API 34+)
|
||||
* Android 14 requires special handling for exact alarms.
|
||||
*/
|
||||
private fun scheduleForAndroid14Plus(
|
||||
context: Context,
|
||||
alarmManager: AlarmManager,
|
||||
pendingIntent: PendingIntent,
|
||||
triggerTime: Long
|
||||
) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (alarmManager.canScheduleExactAlarms()) {
|
||||
alarmManager.setExactAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
triggerTime,
|
||||
pendingIntent
|
||||
)
|
||||
Log.d(TAG, "Alarm scheduled using setExactAndAllowWhileIdle (API 34+)")
|
||||
} else {
|
||||
// Fallback to inexact alarm if permission not granted
|
||||
alarmManager.setAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
triggerTime,
|
||||
pendingIntent
|
||||
)
|
||||
Log.w(TAG, "Exact alarm permission not granted, using setAndAllowWhileIdle (API 34+)")
|
||||
}
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "SecurityException scheduling alarm: ${e.message}")
|
||||
// Fallback to inexact alarm
|
||||
alarmManager.setAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
triggerTime,
|
||||
pendingIntent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule alarm for Android 12-13 (API 31-33)
|
||||
*/
|
||||
private fun scheduleForAndroid12To13(
|
||||
context: Context,
|
||||
alarmManager: AlarmManager,
|
||||
pendingIntent: PendingIntent,
|
||||
triggerTime: Long
|
||||
) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (alarmManager.canScheduleExactAlarms()) {
|
||||
alarmManager.setExactAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
triggerTime,
|
||||
pendingIntent
|
||||
)
|
||||
Log.d(TAG, "Alarm scheduled using setExactAndAllowWhileIdle (API 31-33)")
|
||||
} else {
|
||||
alarmManager.setAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
triggerTime,
|
||||
pendingIntent
|
||||
)
|
||||
Log.w(TAG, "Exact alarm permission not granted, using setAndAllowWhileIdle (API 31-33)")
|
||||
}
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "SecurityException scheduling alarm: ${e.message}")
|
||||
alarmManager.setAndAllowWhileIdle(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
triggerTime,
|
||||
pendingIntent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the scheduled restart alarm.
|
||||
*/
|
||||
fun cancelRestartAlarm(context: Context) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
|
||||
val intent = Intent(context, RestartAlarmReceiver::class.java).apply {
|
||||
action = RestartAlarmReceiver.ACTION_SCHEDULED_RESTART
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
RESTART_ALARM_REQUEST_CODE,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
alarmManager.cancel(pendingIntent)
|
||||
Log.d(TAG, "Restart alarm cancelled")
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the app can schedule exact alarms.
|
||||
* Returns true for Android < 12 (always allowed) or if permission is granted on Android 12+.
|
||||
*/
|
||||
fun canScheduleExactAlarms(context: Context): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
alarmManager.canScheduleExactAlarms()
|
||||
} else {
|
||||
true // Always allowed on older versions
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open system settings to request exact alarm permission (Android 12+).
|
||||
*/
|
||||
fun requestExactAlarmPermission(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error opening exact alarm settings: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
package com.example.hmg_qline.hmg_qline
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
* BroadcastReceiver for handling scheduled app restart alarms.
|
||||
* This is triggered by AlarmManager at the scheduled time (e.g., 12:15 AM).
|
||||
* Compatible with Android 14+ (API 34+) and older versions.
|
||||
*/
|
||||
class RestartAlarmReceiver : BroadcastReceiver() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RestartAlarmReceiver"
|
||||
const val ACTION_SCHEDULED_RESTART = "com.example.hmg_qline.SCHEDULED_RESTART"
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Log.d(TAG, "Received intent: ${intent.action}")
|
||||
|
||||
when (intent.action) {
|
||||
ACTION_SCHEDULED_RESTART -> {
|
||||
Log.d(TAG, "Scheduled restart triggered")
|
||||
launchApp(context)
|
||||
|
||||
// Re-schedule the alarm for the next day
|
||||
rescheduleAlarm(context)
|
||||
}
|
||||
Intent.ACTION_BOOT_COMPLETED,
|
||||
"android.intent.action.QUICKBOOT_POWERON",
|
||||
"com.htc.intent.action.QUICKBOOT_POWERON" -> {
|
||||
Log.d(TAG, "Boot completed - re-scheduling daily restart alarm")
|
||||
rescheduleAlarm(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchApp(context: Context) {
|
||||
try {
|
||||
Log.d(TAG, "Launching app via foreground service")
|
||||
|
||||
val serviceIntent = Intent(context, BootForegroundService::class.java).apply {
|
||||
putExtra("source", "scheduled_restart")
|
||||
}
|
||||
|
||||
// Use foreground service for Android 8.0+ (API 26+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(serviceIntent)
|
||||
} else {
|
||||
context.startService(serviceIntent)
|
||||
}
|
||||
|
||||
Log.d(TAG, "App launch initiated successfully")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error launching app: ${e.message}")
|
||||
|
||||
// Fallback: try direct activity launch
|
||||
try {
|
||||
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
context.startActivity(launchIntent)
|
||||
} catch (fallbackError: Exception) {
|
||||
Log.e(TAG, "Fallback launch also failed: ${fallbackError.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rescheduleAlarm(context: Context) {
|
||||
try {
|
||||
// Re-schedule for the next day at 00:15
|
||||
AlarmScheduler.scheduleRestartAlarm(context, 0, 15)
|
||||
Log.d(TAG, "Alarm rescheduled for next day at 00:15")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error rescheduling alarm: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue