如何在Android Q上设置闹钟? [英] How to set an alarm on Android Q?

查看:144
本文介绍了如何在Android Q上设置闹钟?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Android Q似乎有很多新的限制,但是警报不应该是其中之一:

Android Q seems to have plenty of new restrictions, but alarms shouldn't be one of them:

https://developer.android.com/guide/components/activities/后台启动

似乎我为设置警报而制作的旧代码在P上效果很好,但现在再也无法正常工作了:

It seems that old code that I made for setting an alarm, which worked fine on P, can't work well anymore:

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var manager: AlarmManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        manager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        button.setOnClickListener {
            Log.d("AppLog", "alarm set")
            Toast.makeText(this, "alarm set", Toast.LENGTH_SHORT).show()
            val timeToTrigger = System.currentTimeMillis() + 10 * 1000
            setAlarm(this, timeToTrigger, 1)
        }
    }

    companion object {
        fun setAlarm(context: Context, timeToTrigger: Long, requestId: Int) {
            val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
            when {
                VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP -> manager.setAlarmClock(AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
                VERSION.SDK_INT >= VERSION_CODES.KITKAT -> manager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
                else -> manager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
            }
        }
    }
}

接收器确实获得了意图,但是当它尝试打开活动时,有时什么也没发生:

The receiver does get the Intent, but when it tries to open the Activity, sometimes nothing occurs:

AlarmReceiver.kt

class AlarmReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("AppLog", "AlarmReceiver onReceive")
        context.startActivity(Intent(context, Main2Activity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
    }
}

我将此视为错误,报告了 此处 (包括示例代码)

Seeing this as a bug, I reported here (including sample code)

我试图找到Q上的新内容,以查看可能导致它的原因,但我找不到它.

I tried to find what's new on Q, to see what could cause it, and I couldn't find it.

我还尝试(如果您查看代码)直接打开活动,而不是通过BroadcastReceiver.

I also tried (if you look at the code) to directly open the Activity instead of via a BroadcastReceiver.

而且,我试图将BroadcastReceiver设置为在其他进程上运行.

And, I tried to set the BroadcastReceiver to run on a different process.

所有这些都没有帮助.

我发现的是,尽管某些闹钟应用无法正常运行(例如 及时 ),某些应用可以正常运行(例如"

What I have found is that while some alarm clock apps fail to work properly (such as Timely), some apps work just fine (such as "Alarm Clock Xtreme").

  1. 在Android Q上,有没有官方方法可以让警报正常工作?要打开将显示给用户的活动,就像闹钟应用程序应该显示的一样?

  1. On Android Q, is there an official way to let alarms work correctly? To open an Activity that will be shown to the user, exactly as an alarm clock app should?

我编写的代码有什么问题?它如何在P上起作用,但并不总是在Q上起作用?

What's wrong in the code I've made? How come it works on P but not always on Q?


在被建议在启动活动"并同时使用FullScreenIntent时显示通知后,确定可以工作,但是仅在屏幕关闭时才能工作.当屏幕打开时,它仅显示通知,这是一件坏事,因为整个要点是要向用户显示警报,并且某些用户(例如我)不希望抬起头来.警报通知,在中间弹出但不暂停任何内容.我希望有人能帮上忙,因为这以前很容易做,现在变得太复杂了...


OK after being adviced to have a notification shown while I start the Activity, and also use FullScreenIntent, I got something to work, but it's only working when the screen is turned off. When the screen is turned on, it only shows the notification, which is a bad thing because the whole point is to have an alarm being shown to the user, and some users (like me) don't want to have heads-up-notification for alarms, popping out in the middle of something and not pausing anything. I hope someone can help with this, as this used to be a very easy thing to do, and now it got way too complex...

这是当前代码(在 此处 中可用):

Here's the current code (available here) :

NotificationId

object NotificationId {
    const val ALARM_TRIGGERED = 1
    @JvmStatic
    private var hasInitialized = false

    @UiThread
    @JvmStatic
    fun initNotificationsChannels(context: Context) {
        if (hasInitialized || Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
            return
        hasInitialized = true
        val channelsToUpdateOrAdd = HashMap<String, NotificationChannel>()
        val channel = NotificationChannel(context.getString(R.string.channel_id__alarm_triggered), context.getString(R.string.channel_name__alarm_triggered), NotificationManager.IMPORTANCE_HIGH)
        channel.description = context.getString(R.string.channel_description__alarm_triggered)
        channel.enableLights(true)
        channel.setSound(null, null)
        channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
        channel.enableVibration(false)
        channel.setShowBadge(false)
        channelsToUpdateOrAdd[channel.id] = channel
        //
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val existingChannels = notificationManager.notificationChannels
        if (existingChannels != null)
            for (existingChannel in existingChannels) {
                //                The importance of an existing channel will only be changed if the new importance is lower than the current value and the user has not altered any settings on this channel.
                //                The group an existing channel will only be changed if the channel does not already belong to a group. All other fields are ignored for channels that already exist.
                val channelToUpdateOrAdd = channelsToUpdateOrAdd[existingChannel.id]
                if (channelToUpdateOrAdd == null) //|| channelToUpdateOrAdd.importance > existingChannel.importance || (existingChannel.group != null && channelToUpdateOrAdd.group != existingChannel.group))
                    notificationManager.deleteNotificationChannel(existingChannel.id)
            }
        for (notificationChannel in channelsToUpdateOrAdd.values) {
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }
}

MyService.kt

class MyService : Service() {
    override fun onBind(p0: Intent?): IBinder? = null
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d("AppLog", "MyService onStartCommand")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationId.initNotificationsChannels(this)
            val builder = NotificationCompat.Builder(this, getString(R.string.channel_id__alarm_triggered)).setSmallIcon(android.R.drawable.sym_def_app_icon) //
                    .setPriority(NotificationCompat.PRIORITY_HIGH).setCategory(NotificationCompat.CATEGORY_ALARM)
            builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            builder.setShowWhen(false)
            builder.setContentText("Alarm is triggered!")
            builder.setContentTitle("Alarm!!!")
            val fullScreenIntent = Intent(this, Main2Activity::class.java)
            val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
                    fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
            builder.setFullScreenIntent(fullScreenPendingIntent, true)
            startForeground(NotificationId.ALARM_TRIGGERED, builder.build())
            startActivity(Intent(this, Main2Activity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
            Handler().postDelayed({
                stopForeground(true)
                stopSelf()
            }, 2000L)
        } else {
            startActivity(Intent(this, Main2Activity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
        }
        return super.onStartCommand(intent, flags, startId)
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var manager: AlarmManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        manager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        button.setOnClickListener {
            Log.d("AppLog", "alarm set")
            Toast.makeText(this, "alarm set", Toast.LENGTH_SHORT).show()
            val timeToTrigger = System.currentTimeMillis() + 10 * 1000
            setAlarm(this, timeToTrigger, 1)
        }
    }

    companion object {
        fun setAlarm(context: Context, timeToTrigger: Long, requestId: Int) {
            val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
                        val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
            //            val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, Main2Activity::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
//            val pendingIntent = PendingIntent.getService(context, requestId, Intent(context, MyService::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
            when {
                VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP -> manager.setAlarmClock(AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
                VERSION.SDK_INT >= VERSION_CODES.KITKAT -> manager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
                else -> manager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
            }
        }
    }
}

AlarmReceiver.kt

class AlarmReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("AppLog", "AlarmReceiver onReceive")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(Intent(context, MyService::class.java))
        } else context.startService(Intent(context, MyService::class.java))
    }
}

推荐答案

我编写的代码有什么问题?它如何在P上起作用,但并不总是在Q上起作用?

What's wrong in the code I've made? How come it works on P but not always on Q?

您正在尝试从后台开始活动. 在大多数情况下,Android 10+上均禁止该功能.

You are attempting to start an activity from the background. That is banned on Android 10+ for the most part.

根据文档,警报不应受到损害.

According to the docs, alarms shouldn't be harmed.

从您引用的材料中,重点是:应用程序从系统接收到通知 PendingIntent".您没有使用通知.因此,此例外不适用.

From the material that you quoted, with emphasis added: "The app receives a notification PendingIntent from the system". You are not using notifications. And, therefore, this exception does not apply.

在Android Q上,有没有一种官方方法可以让闹钟正常工作?要打开将显示给用户的活动,就像闹钟应用程序应该显示的一样?

On Android Q, is there an official way to let alarms work correctly? To open an Activity that will be shown to the user, exactly as an alarm clock app should?

使用全屏显示Intent的通知,如文档.如果屏幕被锁定,则在发出通知时将显示您的活动.如果屏幕未锁定,则会显示高优先级(抬起头")通知.换句话说:

Use a notification with a full-screen Intent, as is covered in the documentation. If the screen is locked, your activity will be displayed when the notification is raised. If the screen is unlocked, a high-priority ("heads up") notification will be displayed instead. In other words:

  • 如果不使用设备,您将得到想要的东西

  • If the device is not being used, you get what you want

如果可能正在使用该设备,则用户无需您接管屏幕即可了解事件,因此您不会干扰用户的操作(例如,在驾驶时依靠导航应用程序)

If the device is probably being used, the user find out about the event without your taking over the screen, so you do not interfere with whatever the user is doing (e.g., relying on a navigation app while driving)

这篇关于如何在Android Q上设置闹钟?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆