| | |
| | | import android.content.Intent |
| | | import android.os.Build |
| | | import androidx.core.app.NotificationCompat |
| | | import androidx.work.* |
| | | import androidx.work.CoroutineWorker |
| | | import androidx.work.WorkerParameters |
| | | import com.example.firstapp.MainActivity |
| | |
| | | import kotlinx.coroutines.Dispatchers |
| | | import kotlinx.coroutines.flow.first |
| | | import kotlinx.coroutines.withContext |
| | | import java.util.Calendar |
| | | import java.util.concurrent.TimeUnit |
| | | |
| | | /** |
| | |
| | | override suspend fun doWork(): Result = withContext(Dispatchers.IO) { |
| | | android.util.Log.d("ReminderWorker", "doWork 开始执行") |
| | | try { |
| | | // 获取当前时间 |
| | | val currentTime = System.currentTimeMillis() |
| | | val scheduledTime = inputData.getLong("scheduled_time", 0) |
| | | |
| | | // 如果有设定的预定时间且当前时间与预定时间相差超过30分钟,则跳过执行 |
| | | if (scheduledTime > 0 && Math.abs(currentTime - scheduledTime) > 30 * 60 * 1000) { |
| | | android.util.Log.d("ReminderWorker", "当前时间与预定时间相差过大,跳过执行") |
| | | return@withContext Result.success() |
| | | } |
| | | |
| | | // 获取所有提醒设置 |
| | | val reminderList = reminderRepository.getAllReminders().first() |
| | | android.util.Log.d("ReminderWorker", "获取到 ${reminderList.size} 条提醒设置") |
| | | |
| | | if (reminderList.isEmpty()) { |
| | | // 测试用:如果没有数据,也发送一条测试通知 |
| | | sendTestNotification() |
| | | android.util.Log.d("ReminderWorker", "数据为空,发送了测试通知") |
| | | } |
| | | |
| | | for (reminder in reminderList) { |
| | | try { |
| | | android.util.Log.d("ReminderWorker", "处理提醒: ${reminder.categoryName}") |
| | | checkCategoryContent(reminder.categoryId, reminder.categoryName, reminder.notificationMethod) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | android.util.Log.e("ReminderWorker", "处理提醒失败: ${e.message}") |
| | | if (reminderList.isNotEmpty()) { |
| | | for (reminder in reminderList) { |
| | | try { |
| | | android.util.Log.d("ReminderWorker", "处理提醒: ${reminder.categoryName}") |
| | | checkCategoryContent(reminder.categoryId, reminder.categoryName, reminder.notificationMethod) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | android.util.Log.e("ReminderWorker", "处理提醒失败: ${e.message}") |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | val channel = NotificationChannel( |
| | | CHANNEL_ID, |
| | | "提醒通知", |
| | | NotificationManager.IMPORTANCE_DEFAULT |
| | | ) |
| | | NotificationManager.IMPORTANCE_HIGH // 提高重要性级别 |
| | | ).apply { |
| | | enableLights(true) // 开启指示灯 |
| | | enableVibration(true) // 开启震动 |
| | | description = "重要提醒通知" // 设置描述 |
| | | } |
| | | notificationManager.createNotificationChannel(channel) |
| | | } |
| | | |
| | |
| | | .setSmallIcon(R.drawable.ic_reminder) // 使用自定义图标 |
| | | .setContentTitle(title) |
| | | .setContentText(content) |
| | | .setPriority(NotificationCompat.PRIORITY_DEFAULT) |
| | | .setPriority(NotificationCompat.PRIORITY_HIGH) // 提高通知优先级 |
| | | .setCategory(NotificationCompat.CATEGORY_REMINDER) // 设置为提醒类别 |
| | | .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) // 在锁屏上显示完整通知 |
| | | .setAutoCancel(true) |
| | | .setContentIntent(pendingIntent) |
| | | .build() |
| | |
| | | |
| | | companion object { |
| | | private const val CHANNEL_ID = "reminder_channel" |
| | | private const val MISSED_PREF_KEY = "last_checked_reminder_time" |
| | | |
| | | // 定时任务执行频率 - 设置为每天运行一次 |
| | | val REPEAT_INTERVAL = 24L // 每24小时运行一次 |
| | | // 定时任务执行频率(不再使用) |
| | | val REPEAT_INTERVAL = 24L // 设置为每24小时运行一次 |
| | | val REPEAT_INTERVAL_TIME_UNIT = TimeUnit.HOURS |
| | | |
| | | // 测试用的频率配置,已注释掉 |
| | | // val REPEAT_INTERVAL = 2L |
| | | // val REPEAT_INTERVAL_TIME_UNIT = TimeUnit.MINUTES |
| | | |
| | | // 创建在指定时间运行的定时任务 |
| | | fun setupScheduledWorker(context: Context, hour: Int, minute: Int) { |
| | | val calendar = Calendar.getInstance() |
| | | val now = Calendar.getInstance() |
| | | |
| | | calendar.set(Calendar.HOUR_OF_DAY, hour) |
| | | calendar.set(Calendar.MINUTE, minute) |
| | | calendar.set(Calendar.SECOND, 0) |
| | | |
| | | // 如果设定时间已经过了,则设置为明天这个时间 |
| | | if (calendar.timeInMillis < now.timeInMillis) { |
| | | calendar.add(Calendar.DAY_OF_MONTH, 1) |
| | | } |
| | | |
| | | // 计算初始延迟 |
| | | val initialDelay = calendar.timeInMillis - now.timeInMillis |
| | | |
| | | // 先取消所有现有的提醒任务 |
| | | WorkManager.getInstance(context).cancelAllWorkByTag("reminder_worker") |
| | | |
| | | // 创建周期性工作请求(每天同一时间) |
| | | val dailyWorkRequest = PeriodicWorkRequestBuilder<ReminderWorker>( |
| | | 24, TimeUnit.HOURS |
| | | ) |
| | | .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS) |
| | | .setConstraints( |
| | | Constraints.Builder() |
| | | .setRequiredNetworkType(NetworkType.CONNECTED) |
| | | .build() |
| | | ) |
| | | // 设置灵活时间窗口为0,确保尽量准时执行 |
| | | .setInputData(Data.Builder().putLong("scheduled_time", calendar.timeInMillis).build()) |
| | | .addTag("reminder_worker") |
| | | .build() |
| | | |
| | | // 使用 REPLACE 策略确保新配置生效 |
| | | WorkManager.getInstance(context).enqueueUniquePeriodicWork( |
| | | "daily_reminder_${hour}_${minute}", |
| | | ExistingPeriodicWorkPolicy.REPLACE, |
| | | dailyWorkRequest |
| | | ) |
| | | |
| | | android.util.Log.d("ReminderWorker", "设置定时提醒: ${hour}:${minute}, 初始延迟: ${initialDelay/1000/60}分钟") |
| | | |
| | | // 记录预定的下次执行时间 |
| | | val prefs = context.getSharedPreferences("reminder_prefs", Context.MODE_PRIVATE) |
| | | prefs.edit().putLong("next_reminder_time_${hour}_${minute}", calendar.timeInMillis).apply() |
| | | } |
| | | |
| | | // 应用启动时检查是否有错过的提醒 |
| | | fun checkMissedReminders(context: Context) { |
| | | val prefs = context.getSharedPreferences("reminder_prefs", Context.MODE_PRIVATE) |
| | | val lastCheckedTime = prefs.getLong(MISSED_PREF_KEY, 0) |
| | | val currentTime = System.currentTimeMillis() |
| | | |
| | | // 如果上次检查时间是0,说明是首次运行应用 |
| | | if (lastCheckedTime == 0L) { |
| | | prefs.edit().putLong(MISSED_PREF_KEY, currentTime).apply() |
| | | return |
| | | } |
| | | |
| | | // 检查是否错过了提醒时间点 |
| | | val lastCheckedCalendar = Calendar.getInstance().apply { timeInMillis = lastCheckedTime } |
| | | val currentCalendar = Calendar.getInstance().apply { timeInMillis = currentTime } |
| | | |
| | | // 如果不是同一天,或者超过了设定的提醒时间点 |
| | | val missedReminder = checkIfMissedReminderTime(lastCheckedCalendar, currentCalendar) |
| | | |
| | | if (missedReminder) { |
| | | android.util.Log.d("ReminderWorker", "检测到错过的提醒,立即执行一次") |
| | | // 立即安排一次性任务执行提醒检查 |
| | | val oneTimeRequest = OneTimeWorkRequestBuilder<ReminderWorker>() |
| | | .setConstraints( |
| | | Constraints.Builder() |
| | | .setRequiredNetworkType(NetworkType.CONNECTED) |
| | | .build() |
| | | ) |
| | | .build() |
| | | |
| | | WorkManager.getInstance(context).enqueue(oneTimeRequest) |
| | | } |
| | | |
| | | // 更新最后检查时间 |
| | | prefs.edit().putLong(MISSED_PREF_KEY, currentTime).apply() |
| | | } |
| | | |
| | | // 检查是否错过了提醒时间点 |
| | | private fun checkIfMissedReminderTime(lastChecked: Calendar, current: Calendar): Boolean { |
| | | // 如果日期不同,说明至少过了一天 |
| | | if (lastChecked.get(Calendar.DAY_OF_YEAR) != current.get(Calendar.DAY_OF_YEAR) || |
| | | lastChecked.get(Calendar.YEAR) != current.get(Calendar.YEAR)) { |
| | | |
| | | // 检查当前时间是否超过了9:00或13:50 |
| | | val currentHour = current.get(Calendar.HOUR_OF_DAY) |
| | | val currentMinute = current.get(Calendar.MINUTE) |
| | | |
| | | return (currentHour > 9 || (currentHour == 9 && currentMinute >= 0)) || |
| | | (currentHour > 13 || (currentHour == 13 && currentMinute >= 50)) |
| | | } |
| | | |
| | | // 如果同一天,检查是否在上次检查后经过了9:00或13:50时间点 |
| | | val lastHour = lastChecked.get(Calendar.HOUR_OF_DAY) |
| | | val lastMinute = lastChecked.get(Calendar.MINUTE) |
| | | val currentHour = current.get(Calendar.HOUR_OF_DAY) |
| | | val currentMinute = current.get(Calendar.MINUTE) |
| | | |
| | | // 检查是否错过了9:00 |
| | | if ((lastHour < 9 || (lastHour == 9 && lastMinute == 0)) && |
| | | (currentHour > 9 || (currentHour == 9 && currentMinute > 0))) { |
| | | return true |
| | | } |
| | | |
| | | // 检查是否错过了13:50 |
| | | if ((lastHour < 13 || (lastHour == 13 && lastMinute < 50)) && |
| | | (currentHour > 13 || (currentHour == 13 && currentMinute >= 50))) { |
| | | return true |
| | | } |
| | | |
| | | return false |
| | | } |
| | | } |
| | | } |