18

Have been trying to solve this but couldn't . this is udacity lesson where i am trying to create notifications using pending intent but there is problem with Pending Intent being Mutable i tried with this way

ViewModel

private val REQUEST_CODE = 0
private val TRIGGER_TIME = "TRIGGER_AT"

private val minute: Long = 60_000L
private val second: Long = 1_000L

private val timerLengthOptions: IntArray
private val notifyPendingIntent: PendingIntent

private val alarmManager = app.getSystemService(Context.ALARM_SERVICE) as AlarmManager
private var prefs =
    app.getSharedPreferences("com.shivaConsulting.androidProjects.kotlinnotificatons", Context.MODE_PRIVATE)
private val notifyIntent = Intent(app, AlarmReciever::class.java)

private val _timeSelection = MutableLiveData<Int>()
val timeSelection: LiveData<Int>
    get() = _timeSelection

private val _elapsedTime = MutableLiveData<Long>()
val elapsedTime: LiveData<Long>
    get() = _elapsedTime

private var _alarmOn = MutableLiveData<Boolean>()
val isAlarmOn: LiveData<Boolean>
    get() = _alarmOn


private lateinit var timer: CountDownTimer


init {
    _alarmOn.value = PendingIntent.getBroadcast(
        getApplication(),
        REQUEST_CODE,
        notifyIntent,
        PendingIntent.FLAG_NO_CREATE
    ) != null

    notifyPendingIntent = PendingIntent.getBroadcast(
        getApplication(),
        REQUEST_CODE,
        notifyIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )

    timerLengthOptions = app.resources.getIntArray(R.array.minutes_array)

    //If alarm is not null, resume the timer back for this alarm
    if (_alarmOn.value!!) {
        createTimer()
    }

}

/**
 * Turns on or off the alarm
 *
 * @param isChecked, alarm status to be set.
 */
fun setAlarm(isChecked: Boolean) {
    when (isChecked) {
        true -> timeSelection.value?.let { startTimer(it) }
        false -> cancelNotification()
    }
}

/**
 * Sets the desired interval for the alarm
 *
 * @param timerLengthSelection, interval timerLengthSelection value.
 */
fun setTimeSelected(timerLengthSelection: Int) {
    _timeSelection.value = timerLengthSelection
}

/**
 * Creates a new alarm, notification and timer
 */
private fun startTimer(timerLengthSelection: Int) {
    _alarmOn.value?.let {
        if (!it) {
            _alarmOn.value = true
            val selectedInterval = when (timerLengthSelection) {
                0 -> second * 10 //For testing only
                else ->timerLengthOptions[timerLengthSelection] * minute
            }
            val triggerTime = SystemClock.elapsedRealtime() + selectedInterval

            // TODO: Step 1.5 get an instance of NotificationManager and call sendNotification

            // TODO: Step 1.15 call cancel notification

            AlarmManagerCompat.setExactAndAllowWhileIdle(
                alarmManager,
                AlarmManager.ELAPSED_REALTIME_WAKEUP,
                triggerTime,
                notifyPendingIntent
            )

            viewModelScope.launch {
                saveTime(triggerTime)
            }
        }
    }
    createTimer()
}

/**
 * Creates a new timer
 */
private fun createTimer() {
    viewModelScope.launch {
        val triggerTime = loadTime()
        timer = object : CountDownTimer(triggerTime, second) {
            override fun onTick(millisUntilFinished: Long) {
                _elapsedTime.value = triggerTime - SystemClock.elapsedRealtime()
                if (_elapsedTime.value!! <= 0) {
                    resetTimer()
                }
            }

            override fun onFinish() {
                resetTimer()
            }
        }
        timer.start()
    }
}

/**
 * Cancels the alarm, notification and resets the timer
 */
private fun cancelNotification() {
    resetTimer()
    alarmManager.cancel(notifyPendingIntent)
}

/**
 * Resets the timer on screen and sets alarm value false
 */
private fun resetTimer() {
    timer.cancel()
    _elapsedTime.value = 0
    _alarmOn.value = false
}

private suspend fun saveTime(triggerTime: Long) =
    withContext(Dispatchers.IO) {
        prefs.edit().putLong(TRIGGER_TIME, triggerTime).apply()
    }

private suspend fun loadTime(): Long =
    withContext(Dispatchers.IO) {
        prefs.getLong(TRIGGER_TIME, 0)
    }'''

Fragment

class BlankFragment : Fragment() {

companion object {
    fun newInstance() = BlankFragment()
}
private val viewModel : BlankViewModel by lazy {
    ViewModelProvider(this).get(BlankViewModel::class.java)
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val binding = FragmentBlankBinding.inflate(inflater)
    binding.lifecycleOwner = this
    binding.blankViewModel = viewModel
  
    return binding.root
} '''

as i was looking into other similar questions which was solved by the gradle dependencies i have already added that

 implementation 'androidx.work:work-runtime-ktx:2.7.1'  

but showing this error

E/AndroidRuntime: Caused by: java.lang.IllegalArgumentException: com.shivaconsulting.kotlinnotifications: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
    Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
        at android.app.PendingIntent.checkFlags(PendingIntent.java:378)
        at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:648)
        at android.app.PendingIntent.getBroadcast(PendingIntent.java:635)
        at com.shivaconsulting.kotlinnotifications.ui.BlankViewModel.<init>(BlankViewModel.kt:52)
            ... 44 more

Udacity Starter Code

Ashvin solanki
  • 4,802
  • 3
  • 25
  • 65
Shiva s
  • 710
  • 1
  • 6
  • 13
  • Did you tried my solution ?. – Ashvin solanki Jul 11 '22 at 09:49
  • Did you manage this issue ?, @Shiva – Ashvin solanki Aug 15 '22 at 13:47
  • Does this answer your question? [MediaSessionCompat:Targeting S+ (version 31 and above) requires that one of FLAG\_IMMUTABLE or FLAG\_MUTABLE be specified when creating a PendingIntent](https://stackoverflow.com/questions/68473542/mediasessioncompattargeting-s-version-31-and-above-requires-that-one-of-flag) – Bink Sep 26 '22 at 21:28

6 Answers6

19

Ref : https://developer.android.com/reference/android/app/PendingIntent#FLAG_MUTABLE

Flag indicating that the created PendingIntent should be mutable. This flag cannot be combined with FLAG_IMMUTABLE.

Up until Build.VERSION_CODES.R, PendingIntents are assumed to be mutable by default, unless FLAG_IMMUTABLE is set. Starting with Build.VERSION_CODES.S, it will be required to explicitly specify the mutability of PendingIntents on creation with either (@link #FLAG_IMMUTABLE} or FLAG_MUTABLE. It is strongly recommended to use FLAG_IMMUTABLE when creating a PendingIntent. FLAG_MUTABLE should only be used when some functionality relies on modifying the underlying intent, e.g. any PendingIntent that needs to be used with inline reply or bubbles.

    notifyPendingIntent = PendingIntent.getBroadcast(
            getApplication(),
            REQUEST_CODE,
            notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )

The code should be.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
 notifyPendingIntent = PendingIntent.getBroadcast(
            getApplication(),
            REQUEST_CODE,
            notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
        )
    } else {
      notifyPendingIntent = PendingIntent.getBroadcast(
            getApplication(),
            REQUEST_CODE,
            notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )
    }

The same way you have to in

_alarmOn.value = PendingIntent.getBroadcast(
        getApplication(),
        REQUEST_CODE,
        notifyIntent,
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_MUTABLE else PendingIntent.FLAG_NO_CREATE
    ) != null

Edit 2

Also if you are not using PendingIntent and still having crash from system while using other components like MediaSessionCompat etc.

implementation 'androidx.work:work-runtime-ktx:RUNTIME-VERSION'

RUNTIME VERSION : https://developer.android.com/jetpack/androidx/releases/work

Ashvin solanki
  • 4,802
  • 3
  • 25
  • 65
  • can you mention which file needs to change the above lines? – Ayush Katuwal Feb 04 '23 at 13:41
  • @AyushKatuwal if you are using Work Manager, then use `Gradle`, else you have to change the pending intent in notification part where you are posting notification – Ashvin solanki Feb 07 '23 at 06:05
  • I am using reactnative and got this error only on Android version 12+. I used work manager and added the line on gradle file and also made some changes on RNPushNotificationHelper.java file as I searched for PendingIntent and made some changes on PendingIntent.getActivity() and PendingIntent.getBroadcast() but still getting the same error any idea to resolve this issue? – Ayush Katuwal Feb 08 '23 at 07:38
  • And I'm also getting this error, `Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.` in FBActivityEventListener.java file at: `com.facebook.reactnative.androidsdk.FBActivityEventListener.onActivityResult(FBActivityEventListener.java:34)` And this is the function: `public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { mCallbackManager.onActivityResult(requestCode, resultCode, data); }` – Ayush Katuwal Feb 08 '23 at 08:17
  • @AyushKatuwal where are you using this flags ? – Ashvin solanki Feb 08 '23 at 08:45
  • Update the facebook sdk that can also fix your error if that sdk has any issues – Ashvin solanki Feb 08 '23 at 08:47
  • At RNPushNotificationHelper.java – Ayush Katuwal Feb 18 '23 at 01:40
  • 1
    Upgrading "react-native-fbsdk-next" to version "10.1.0" resolves the issue thanks a lot bro for helping me out with this issue – Ayush Katuwal Feb 22 '23 at 03:49
  • 1
    @AyushKatuwal welcome, you did that your own all credits to you :) – Ashvin solanki Feb 22 '23 at 06:21
6

[Solution] Adding below dependency. This is work for me

implementation 'androidx.work:work-runtime:2.7.0-alpha05'
Thilina Chamika
  • 206
  • 3
  • 4
2

this was better way

 private val flags = when{
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
        PendingIntent.FLAG_MUTABLE
    else -> {PendingIntent.FLAG_MUTABLE}
}
Shiva s
  • 710
  • 1
  • 6
  • 13
2

In addition to @Ashvinsolanki answer's , you may need to increase the version of implementation "androidx.work:work-runtime-ktx:2.5.0" to the latest update

mazen amr
  • 90
  • 6
2

[Solution] as mentioned above @Thilina

I updated from 2.7.0-alpha04 to 2.7.0-alpha05 and it worked for me With API 31 and 33

implementation 'androidx.work:work-runtime:2.7.0-alpha05'

-8

If you want the fastest solution, you should modify app/build.gradle inside targetSdkVersion,

The maximum value is 30

linchuan
  • 39
  • 2