设置闹钟()。我们需要一个可行的、当前的和经过验证的使用示例

问题描述 投票:0回答:1

我正在 Android 上制作一个闹钟应用程序。看来我一切都是按照文档做的,但闹钟却从来没有响过。我不想使用 SetExact 方法来设置闹钟,因为根据文档,在我的情况下使用 SetAlarmClock 是合乎逻辑的。我需要一个相关且适用的用例。

在这里,为了以防万一,我将给出我编写的代码示例 - 意图已安装,但不起作用。

设置信号的类:


class AlarmManagement private constructor(private val context: Context) {

     companion object {
         private var instance: AlarmManagement? = null

         @JvmStatic
         fun getInstance(context: Context): AlarmManagement {
             if (instance == null) {
                 instance = AlarmManagement(context.applicationContext)
             }
             return instance!!
         }
     }

     val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
     val database = Database.getInstance(MyApplication.getAppContext())


     fun setOrUpdateAlarm(clock: Clock) {

         fun clockValuesAreCorrectForAlarmSetting(clock: Clock): Boolean {
             if(clock == null) {
                 Camp.log("error alarm", "Clock equal to zero was passed to setOrUpdateAlarm()")
                 return false
             }
             if (clock.isActive == false) return false//this is acceptable, just end the method

             if (clock.id == null) {
                 Camp.log("error alarm", "A clock with id = null was passed to set the alarm")
                 return false
             }
             return true
         }
         if (!clockValuesAreCorrectForAlarmSetting(clock)) return
         if (!exactAlarmIsAllowed()) return

         val alarmIntent = Intent(context, AlarmReceiver::class.java).apply {
         }
         val pendingIntent = PendingIntent.getBroadcast(
             context
             clock.id!!.toInt(),//ALARM_REQUEST_CODE
             alarmIntent,
             PendingIntent.FLAG_UPDATE_CURRENT
         )
         //To use not millisseconds to set the time, but time in a convenient format, use Calendar
         val calendar = Calendar.getInstance().apply {
             set(Calendar.HOUR_OF_DAY, clock.triggeringHour)
             set(Calendar.MINUTE, clock.triggeringMinute)
             if (clock.alarmRepeatingMode == AlarmRepeatingMode.EVERYDAY || clock.alarmRepeatingMode == AlarmRepeatingMode.SELECTDAYS) {
                 add(Calendar.DAY_OF_YEAR, 1) // Set the interval for the day
             }
         }
         val info = AlarmManager.AlarmClockInfo(calendar.timeInMillis, pendingIntent)
         //Warning: on android below 5.0 you need to use ExactAlarm instead of setAlarmClock (I’m making an application for newer versions)
         alarmManager.setAlarmClock(info, pendingIntent)
         Camp.log("alarm", "AlarmClock was set for $clock")
     }

     //Used at application startup to check permission to set fine Alarms (AlarmClock and ExactAlarm)
     fun exactAlarmIsAllowed(): Boolean {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
             if (alarmManager.canScheduleExactAlarms() == true) {
                 return true
             } else {
                 Camp.log(
                     "alarm info",
                     "Check result: permission to run alarms is missing (exact alarms)"
                 )
                 return false
             }
         } else {
             Camp.log(
                 "alarm info",
                 "Check result: there is permission to run alarms (exact alarms)"
             )
             return true
         }
     }

     //Restarts the intents of all active alarms
     fun reloadAllActiveClocksAlarmIntents() {
         val clocks = database.getAllClocks()
         clocks.forEach {
             setOrUpdateAlarm(it)
         }
     }

     /** Cancel AlarmIntent by intentRequestCode
     I am using clock.id as requestCode at the same time*/
     fun cancelAlarmIntent(intentRequestCode: Int) {
         //Create a new empty intent with the same requestCode as the one you want to cancel
         //In this case, a replacement occurs (the previous one is deleted)
         val alarmIntent = Intent(context, AlarmReceiver::class.java).apply {}
         val pendingIntent = PendingIntent.getBroadcast(
             context
             intentRequestCode,
             alarmIntent,
             PendingIntent.FLAG_UPDATE_CURRENT
         )
         //Cancel empty intent
         pendingIntent.cancel()
     }
}

Receiver 类,在触发时接收它们(SetAlarmClock() 的意图甚至不被接受,尽管 SetExact 被接受)。可能没有必要在这里包含它的代码,因为意图没有达到它,但仍然:


class AlarmReceiver : BroadcastReceiver() {

     /* val alarmsManager = AlarmManagement.getInstance(*//*MyApplication.getAppContext()*//*)*/
     lateinit var triggeringClock: Clock

     val intentProcessingCompletionMessage = "Intent processing has completed."

     // This method is called when the BroadcastReceiver is receiving an Intent broadcast.
     override fun onReceive(context: Context, intent: Intent) {

         if (intent.action == "ACTION_START_ALARM") {
             Camp.log("alarm","AlarmReceiverreceived alarm intent")

             val calendar = Calendar.getInstance()
             val today = calendar.get(Calendar.DAY_OF_WEEK)
             val database = Database.getInstance(MyApplication.getAppContext())

             fun intentProcessing() {

                 fun gettingClock(): Clock? {

                     val intentClockId = intent.getSerializableExtra("clockId") as Long
                     if (intentClockId == null) {
                         Camp.log("error alarm", "Intent clock id == null")
                         return null
                     }

                     val dbClock = database.getClockById(intentClockId)
                     if (dbClock == null) {
                         Camp.log("error alarm", "Clock with the same id as intentClock was not found")
                         return null
                     }

                     return dbClock

                 }

                 val clock = gettingClock()
                 if (clock == null) {
                     Camp.log(
                         "error alarm",
                         "An error occurred while retrieving the alarm object. $intentProcessingCompletionMessage"
                     )
                     return
                 }

                 if(!clock.isActive){
                     Camp.log(
                         "error alarm",
                         "For an unexpected reason, an inactive alarm was triggered. The intent for this alarm has been canceled and will no longer fire. $intentProcessingCompletionMessage"
                     )
                     return
                 }

                 fun todayIsRightDayForAlarm(): Boolean {
                     if (clock.alarmRepeatingMode == AlarmRepeatingMode.ONETIME || clock.alarmRepeatingMode == AlarmRepeatingMode.EVERYDAY) {
                         return true
                     }
                     if (clock.alarmRepeatingMode == AlarmRepeatingMode.SELECTDAYS) {
                         //Calendar.DAY_OF_WEEK week starts on Sun (Sun == 1, Mon == 2, etc.), so we convert it to (1 shl (today - 1))
                         return (clock.triggeringWeekDays?.and((1 shl (today - 1)))) != 0 //check for the presence of today in the triggeringWeekDays combination using bitwise multiplication
                     }
                     Camp.log(
                         "error alarm",
                         "Checking for the day of the week to trigger this type of alarm was not provided"
                     )
                     return false
                 }
                 if (!todayIsRightDayForAlarm()) {
                     Camp.log(
                         "Alarm info"
                         "The current day of the week does not match the days of the received alarm. $intentProcessingCompletionMessage"
                     )
                     return
                 }
                 fun activateAlarm(triggeringClock: Clock) {

                     when (triggeringClock.alarmRepeatingMode) {
                         AlarmRepeatingMode.ONETIME -> {
                             triggeringClock.isActive = false
                             database.insertOrUpdateClock_IdOrResult(triggeringClock)
                         }
                         AlarmRepeatingMode.EVERYDAY, AlarmRepeatingMode.SELECTDAYS -> {
                           
                         }
                     }

                     Camp.log("Alarm info", "Alarm starting. LockScreenActivity started. $intentProcessingCompletionMessage")
                     MyApplication.getInstance().startLockScreenActivity(triggeringClock)
                 }
                 activateAlarm(clock)
                 return
             }
             intentProcessing()
         }

         //Triggered when the device is rebooted
         if (intent.action == "android.intent.action.BOOT_COMPLETED") {
             AlarmManagement.getInstance(MyApplication.getAppContext()).reloadAllActiveClocksAlarmIntents()
         }
     }
}

清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">

     <!--To use ExactAlarms and SetAlarmClock-->
     <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

     <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

     <!--AlarmClock needs to continue working even after the device is rebooted-->
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

     <!--Permission to display pop-up windows (activity)qa when the application is running in the background-->
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

     <uses-permission android:name="android.permission.VIBRATE" />
     <application
         android:name=".MyApplication"

         android:allowBackup="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
         android:fullBackupContent="@xml/backup_rules"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
         android:theme="@style/Base.Theme.PasswordAlarmClock"
         tools:targetApi="31">

<!-- Declare the Worker class for the WorkManager -->
         <service
             android:name=".MyWorker"
             android:exported="false"
             android:permission="android.permission.BIND_JOB_SERVICE">
             <intent-filter>
                 <action android:name="androidx.work.Worker" />
             </intent-filter>
         </service>
<!-- ... -->

<!--Declare an unkillable service for precise notifications-->
         <!--android:foregroundServiceType=""//I don’t know what type should be used and whether it is necessary-->
         <service
             android:name=".AlarmService" />

<!-- <receiver
             android:name=".AlarmReceiver"
             android:enabled="true"
             android:exported="true" />-->
         <receiver android:name=".AlarmReceiver"
             android:exported="false">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </receiver>


         <activity
             android:name=".MainActivity"
             android:exported="true">
         </activity>
         <activity
             android:name=".ClockSettingActivity"
             android:exported="false">
         </activity>
         <activity
             android:name=".SettingsActivity"
             android:exported="true">
         </activity>

<!-- android:windowSoftInputMode="stateHidden" is used to prevent the keyboard from appearing automatically (due to the use of an invisible zone for the input field)-->
         <activity
             android:name=".LockScreenActivity"
             android:exported="true"
             android:windowSoftInputMode="stateHidden">
             <!--This block makes the activity start-->
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />

                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
     </application>

</manifest>
kotlin alarmmanager android-alarms setalarmclock
1个回答
0
投票

在没有服务的情况下尝试时。在模拟器上一切正常,但在小米上不行。 可以使用 FLAG_UPDATE_CURRENT 进行更新,然后在启动新意图时无需取消。

报警管理


class AlarmManagement private constructor(/*private val context: Context*/) {

    companion object {
        private var instance: AlarmManagement? = null

        @JvmStatic
        fun getInstance(): AlarmManagement {
            if (instance == null) {
                instance = AlarmManagement()
            }
            return instance!!
        }
    }

    val alarmManager = MyApplication.getAppContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val database = Database.getInstance(MyApplication.getAppContext())

    val exactAlarmSettingStrategy: ExactAlarmSettingStrategy = SetAlarmClock()

    fun setOrUpdateAlarm(clock: Clock) {

        fun clockValuesAreCorrectForAlarmSetting(): Boolean {
            if (!clock.isActive) return false

            if (clock.id == null) {
                Camp.log("error alarm", "A clock with id = null was passed for setting the alarm")
                return false
            }
            return true
        }
        if (!clockValuesAreCorrectForAlarmSetting()) return
        val requestCode = clock.id!!.toInt()

        cancelAlarmIntent(requestCode)

        val alarmIntent = Intent(MyApplication.getAppContext(), AlarmReceiver::class.java).apply {
            action = "ALARM"
            putExtra("clockId", clock.id)
        }

        if (!exactAlarmIsAllowed()) return

        val pendingIntent = PendingIntent.getBroadcast(
            MyApplication.getAppContext(),
            requestCode,
            alarmIntent,
            PendingIntent.FLAG_IMMUTABLE
        )

        val calendar = java.util.Calendar.getInstance().apply {
            set(Calendar.HOUR_OF_DAY, clock.triggeringHour)
            set(Calendar.MINUTE, clock.triggeringMinute)
        }
        if (calendar.timeInMillis <= System.currentTimeMillis()) {
            calendar.add(Calendar.DAY_OF_YEAR, 1)
        }

        exactAlarmSettingStrategy.setExactAlarm(alarmManager, pendingIntent, calendar)

        Camp.log("alarm", "AlarmClock was set for $clock")
    }

    fun exactAlarmIsAllowed(): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            return if (alarmManager.canScheduleExactAlarms()) {
                true
            } else {
                Camp.log(
                    "alarm info",
                    "Check result: permission for scheduling exact alarms is missing"
                )
                false
            }
        } else {
            Camp.log(
                "alarm info",
                "Check result: permission for scheduling exact alarms is present"
            )
            return true
        }
    }

    fun activateInactiveAlarmIntentsWhoseClocksAreActive() {
        val clocks = database.getAllClocks()
        clocks.forEach {
            setOrUpdateAlarm(it)
        }
    }

    fun cancelAlarmIntent(intentRequestCode: Int) {
       val intent = Intent(MyApplication.getAppContext(), AlarmReceiver::class.java)

        val pendingIntent =
            PendingIntent.getService(MyApplication.getAppContext(), intentRequestCode, intent,
                PendingIntent.FLAG_NO_CREATE)
        if (pendingIntent != null) {
            alarmManager.cancel(pendingIntent)
        }
    }
}

接收器


package com.camporation.passwordalarmclock

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import java.util.Calendar

class AlarmReceiver : BroadcastReceiver() {

    lateinit var triggeringClock: Clock

    val intentProcessingCompletionMessage = "Intent processing is complete. "

    override fun onReceive(context: Context, intent: Intent) {
        Camp.log("AlarmReceiver received intent")
        if (intent.action == "ALARM") {
            Camp.log("alarm","AlarmReceiver received alarm intent")

            val calendar = Calendar.getInstance()
            val today = calendar.get(Calendar.DAY_OF_WEEK)
            val database = Database.getInstance(MyApplication.getAppContext())

            fun intentProcessing() {

                fun gettingClock(): Clock? {

                    val intentClockId = intent.getSerializableExtra("clockId") as Long?
                    if (intentClockId == null) {
                        Camp.log("error alarm", "Intent clock id == null")
                        return null
                    }

                    val dbClock = database.getClockById(intentClockId)
                    if (dbClock == null) {
                        Camp.log("error alarm", "Clock with the same id as intentClock was not found")
                        return null
                    }

                    return dbClock

                }

                val clock = gettingClock()
                if (clock == null) {
                    Camp.log(
                        "error alarm",
                        "An error occurred while obtaining the alarm object. $intentProcessingCompletionMessage"
                    )
                    return
                }

                if(!clock.isActive){
                    Camp.log(
                        "error alarm",
                        "For an unforeseen reason, an inactive alarm went off. The intent of this alarm is canceled and will not trigger anymore. $intentProcessingCompletionMessage"
                    )
                    return
                }

                fun todayIsRightDayForAlarm(): Boolean {
                    if (clock.alarmRepeatingMode == AlarmRepeatingMode.ONETIME || clock.alarmRepeatingMode == AlarmRepeatingMode.EVERYDAY) {
                        return true
                    }
                    if (clock.alarmRepeatingMode == AlarmRepeatingMode.SELECTDAYS) {
                        return (clock.triggeringWeekDays?.and((1 shl (today - 1)))) != 0
                    }
                    Camp.log(
                        "error alarm",
                        "Checking the day of the week for triggering this type of alarm was not provided"
                    )
                    return false
                }
                if (!todayIsRightDayForAlarm()) {
                    Camp.log(
                        "Alarm info",
                        "The current day of the week does not match the triggering days of the accepted alarm. $intentProcessingCompletionMessage"
                    )
                    return
                }
                fun updateClockInDatabase(){
                    when (clock.alarmRepeatingMode) {
                        AlarmRepeatingMode.ONETIME -> {
                            clock.isActive = false
                            val idOrResult = database.insertOrUpdateClock_IdOrResult(clock)
                            Camp.log("One-time alarm modified: isActive = false. It is added to the database with id/result: $idOrResult")
                        }
                        AlarmRepeatingMode.EVERYDAY, AlarmRepeatingMode.SELECTDAYS -> {
                            // currently not required for these types
                        }
                    }
                }
                updateClockInDatabase()
                MainActivity.currentActivity?.onDatabaseUpdatingInReceiver()

                AlarmManagement.getInstance().activateInactiveAlarmIntentsWhoseClocksAreActive()
                Camp.log("alarm allAlarms receiver","The Receiver activated all inactive intents with active alarms")
                fun activateAlarm() {
                    Camp.log("Alarm info", "Triggering the alarm. Starting the LockScreenActivity. $intentProcessingCompletionMessage")
                    MyApplication.getInstance().startLockScreenActivity(clock)
                }
                activateAlarm()
                return
            }
            intentProcessing()
        }

        if (intent.action == "android.intent.action.BOOT_COMPLETED") {
            AlarmManagement.getInstance().activateInactiveAlarmIntentsWhoseClocksAreActive()
        }
    }
}

准确的警报设置


interface ExactAlarmSettingStrategy {
    fun setExactAlarm(alarmManager: AlarmManager, pendingIntent: PendingIntent, calendar: Calendar)
}

class SetAlarmClock : ExactAlarmSettingStrategy {

    override fun setExactAlarm(
        alarmManager: AlarmManager,
        pendingIntent: PendingIntent,
        calendar: Calendar
    ) {
        val info = AlarmManager.AlarmClockInfo(calendar.timeInMillis, pendingIntent)
        alarmManager.setAlarmClock(info, pendingIntent)
    }
}

class SetExactAndAllowWhileIdle : ExactAlarmSettingStrategy {

    override fun setExactAlarm(
        alarmManager: AlarmManager,
        pendingIntent: PendingIntent,
        calendar: Calendar
    ) {
        alarmManager.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            calendar.timeInMillis,
            pendingIntent
        )
    }
}

清单

xml

    <!--  For using ExactAlarms and SetAlarmClock  -->
    <!--  https://developer.android.com/develop/background-work/services/alarms/schedule#exact-permission-declare  -->
    <!--  Requires either SCHEDULE_EXACT_ALARM or USE_EXACT_ALARM  -->
    <!--  Check permission existence using canScheduleExactAlarms()  -->
    <!--  Provided manually for Android 12 and above  -->
    <!--  May be revoked by the user  -->
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>

    <!--  Provided automatically, available from Android 13 and above  -->
    <!--  Cannot be revoked by the user  -->
    <!--  Cannot use (not critical), as working with earlier Android versions  -->
    <!--  <uses-permission android:name="android.permission.USE_EXACT_ALARM"/>  -->


    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>

    <receiver android:name=".AlarmReceiver" android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </receiver>

    ```
© www.soinside.com 2019 - 2024. All rights reserved.