onpreferenceClick and sending Broadcast on Android 14

366 views Asked by At

in our app we have implemented several Preferences of which two have a "Button" functionality. In the onPreferenceClick() of aforementioned Preference the only thing that is done is send a Broadcast which then is actually received by a foreground Service which uses it's value to do someting with the app's foreground notification.

Unfortunately this stopped working with Android 14 Beta. When debugging I actually can see the onPreferenceClick() being called, but afterwards it's kinda of a black box. The Service does NOT receive the broadcast, and after the sendBroadcast() call within the onPreferenceClick I do not see any errors or warnings from the Android System in Logcat.

I gave a try by changing the intent passed to the sendBroadcast() to an implicit one instead of the one defined in the Preferences .xml file, but to no avail :(

<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
                                  xmlns:android="http://schemas.android.com/apk/res/android"
                                  xmlns:tools="http://schemas.android.com/tools">
<PreferenceCategory
        app:title="@string/pref_category_debug"
        app:iconSpaceReserved="false">
<nl.mycompany.myapp.preference.CheckinPreference
            android:enabled="true"
            android:key="about_button_debug_checkin"
            android:persistent="false"
            android:title="@string/about_button_checkin"
            app:iconSpaceReserved="false"
            android:layout_width="wrap_content">
        <intent android:action="mycompany.myapp.intent.action.CHECKED_IN_STATE_CHANGED">
            <extra
                    android:name="checkedin"
                    android:value="true"/>
        </intent>
    </nl.mycompany.myapp.preference.CheckinPreference>
    <nl.mycompany.myapp.preference.CheckinPreference
            android:enabled="true"
            android:key="about_button_debug_checkout"
            android:persistent="false"
            android:title="@string/about_button_checkout"
            app:iconSpaceReserved="false">
        <intent android:action="mycompany.myapp.intent.action.CHECKOUT_STATE_CHANGED">
            <extra
                    android:name="checkedin"
                    android:value="false"/>
        </intent>
    </mycompany.myapp.preference.CheckinPreference>
</PreferenceCategory>
</androidx.preference.PreferenceScreen>

This is our custom Preference :

class CheckinPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs), Preference.OnPreferenceClickListener {
override fun onPreferenceClick(preference: Preference): Boolean {
    context.sendBroadcast(intent)
    return true
}

init {
    this.onPreferenceClickListener = this
}}
2

There are 2 answers

0
TiGer On

I solved it myself. The problem was not in sending the Broadcast but receiving it. Whilst registering the receiver in our Service I was using the RECEIVER_NOT_EXPORTED flag, that was a mistake. After switching to the RECEIVER_EXPORTED flag the broadcast was received correctly from within the Service.

0
user19499909 On

"Runtime-registered broadcasts receivers must specify export behavior Apps and services that target Android 14 and use context-registered receivers are required to specify a flag to indicate whether or not the receiver should be exported to all other apps on the device: either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED, respectively. This requirement helps protect apps from security vulnerabilities by leveraging the features for these receivers introduced in Android 13." for more info :https://developer.android.com/about/versions/14/behavior-changes-14 , I also found this same problem, this may help.. while sending specify your package aswell
intent.setPackage(packageName) while registering your broadcast make sure you add these flags /// For android 14 val listenToBroadcastsFromOtherApps = false val receiverFlags = if (listenToBroadcastsFromOtherApps) { ContextCompat.RECEIVER_EXPORTED } else { ContextCompat.RECEIVER_NOT_EXPORTED } if (isUpsideDownCake()){ ContextCompat.registerReceiver(this,broadcastReceiver, intentFilter,receiverFlags) }else{ if (isOreo()) { registerReceiver(broadcastReceiver, intentFilter) } }