Im working on an android app which uses notifications frequently.
I chose to set the alarms via setting activity rather then Main Activity but I couldn't manage to find out how to cancel the Alarm Manager via the settingActivity immediately after pressing the switch.
I only found out how to cancel the notifications on the MainActivity, which stops them only after closing and opening the app.
what is the preferred way to do it?
from SettingActivity.kt:
class NotificationPreferenceFragment : PreferenceFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_notification)
setHasOptionsMenu(false)
var switchPref: Preference = findPreference(getString(R.string.pref_notifications_switch_key))
switchPref.onPreferenceChangeListener = OnPreferenceChangeListener { preference, isChecked ->
var toast: Toast = if (isChecked as Boolean) {
Toast.makeText(activity, "switch is ON", Toast.LENGTH_SHORT)
} else {
Toast.makeText(activity, "notifications is OFF", Toast.LENGTH_SHORT)
}
toast.show()
true
}
}
cancel method from MainActivity:
fun cancelAlarm() {
alarmMgr = applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent1 = Intent(applicationContext, AlarmReceiver::class.java)
alarmIntent = PendingIntent.getBroadcast(applicationContext, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT)
alarmMgr!!.cancel(alarmIntent)
}
As said by a commenter, just use BroadcastReceiver
NotificationPreferenceFragment.java
class NotificationPreferenceFragment : PreferenceFragment() {
val broadcaster: LocalBroadcastManager? = null;
override fun onCreate(savedInstanceState: Bundle?) {
broadcaster = LocalBroadcastManager.getInstance(this);
}
// Inside your onPreferenceChangeListener, depends on when you want to call it, either ON or OFF
Intent intent = new Intent("YOUR_DATA_STRING");
intent.putExtra(ANY_EXTRAS_STRING, DATA_ITSELF);
broadcaster.sendBroadcast(intent);
}
MainActivity.java
import android.support.v4.content.LocalBroadcastManager;
override fun onStart() {
super.onStart()
LocalBroadcastManager.getInstance(this).registerReceiver(MYReceiver,
IntentFilter("YOUR_DATA_STRING")
)
}
private val MYReceiver = object : BroadcastReceiver() {
fun onReceive(context: Context, intent: Intent) {
if (intent.extras != null) {
// get any extras if neccessary
// intent.extras!!.getString("ANY_EXTRAS_STRING")
cancelAlarm()
}
}
}
override fun onStop() {
super.onStop()
LocalBroadcastManager.getInstance(this).unregisterReceiver(MYReceiver)
}
Related
i'm writing music app. I have service with MediaPlayer which shows notification with custom view (play, next, back buttons). onClick reaction for these buttons was implemented using broadcasting. Is there a way to get access to MediaPlayer object from service, to use it in my broadcast receiver class? Or should I use bind service?
My Receiver class:
class PlayerNotificationReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if(intent != null) {
val intentAction = intent.action
when(intentAction){
"PLAY_ACTION" -> {
// HOW TO GET ACCESS TO MEDIA PLAYER IN SERVICE ?????
Toast.makeText(context, "PLAY_ACTION", Toast.LENGTH_SHORT).show()
// TODO Change image button image in fragment
}
"BACK_ACTION" -> {
// TODO Notification back button implementation
Toast.makeText(context, "BACK_ACTION", Toast.LENGTH_SHORT).show()
}
"NEXT_ACTION" -> {
// TODO Notification next button implementation
Toast.makeText(context, "NEXT_ACTION", Toast.LENGTH_SHORT).show()
}
"PAUSE_ACTION" -> {
// TODO Notification pause button implementation
Toast.makeText(context, "PAUSE_ACTION", Toast.LENGTH_SHORT).show()
}
}
}
}
}
My Service class:
class PlayerService: Service() {
private var song: Int = 0
var songDuration: Long = 0
private lateinit var playerNotificationReceiver: PlayerNotificationReceiver
private val intentRequestCode: Int = 0
private var mediaPlayer: MediaPlayer = MediaPlayer()
private var isPlaying: Boolean = false
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Broadcast receiver
playerNotificationReceiver = PlayerNotificationReceiver()
// Registering broadcast receiver
registerReceiver(playerNotificationReceiver, IntentFilter(getString(R.string.PLAY_ACTION)))
registerReceiver(playerNotificationReceiver, IntentFilter(getString(R.string.PAUSE_ACTION)))
registerReceiver(playerNotificationReceiver, IntentFilter(getString(R.string.BACK_ACTION)))
registerReceiver(playerNotificationReceiver, IntentFilter(getString(R.string.NEXT_ACTION)))
song = intent?.getIntExtra("song", 0) ?: R.raw.taco_hemingway_europa
// onStartCommand implementation
if (!isPlaying) {
// Creating pending intent for notification
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
intentRequestCode,
intent,
PendingIntent.FLAG_IMMUTABLE
)
// Pending intent with broadcast for custom view back button on click
val nextIntent = Intent(getString(R.string.NEXT_ACTION))
nextIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val nextPendingIntent = PendingIntent.getBroadcast(this, 0, nextIntent, PendingIntent.FLAG_IMMUTABLE)
// Pending intent with broadcast for custom view back button on click
val backIntent = Intent(getString(R.string.BACK_ACTION))
backIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val backPendingIntent = PendingIntent.getBroadcast(this, 0, backIntent, PendingIntent.FLAG_IMMUTABLE)
// Pending intent with broadcast for custom view back button on click
val playIntent = Intent(getString(R.string.PLAY_ACTION))
playIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val playPendingIntent = PendingIntent.getBroadcast(this, 0, playIntent, PendingIntent.FLAG_IMMUTABLE)
// Remote view
val playerNotificationLayout = RemoteViews(packageName, R.layout.player_small_notification)
val playerNotificationLayoutExpanded = RemoteViews(packageName, R.layout.player_large_notification)
// Building notification with custom view
val notificationBuilder = NotificationCompat.Builder(this, PLAYER_CHANNEL)
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.app_notification_icon)
.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(playerNotificationLayout)
.setCustomBigContentView(playerNotificationLayoutExpanded)
.setContentTitle("Title")
.setContentText("Name")
// Setting custom view button on click reaction
playerNotificationLayout.setOnClickPendingIntent(R.id.notificationPlayButton, playPendingIntent)
playerNotificationLayout.setOnClickPendingIntent(R.id.notificationBackButton, backPendingIntent)
playerNotificationLayout.setOnClickPendingIntent(R.id.notificationNextButton, nextPendingIntent)
// Notification manager
val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Creating notification
val notification = notificationBuilder.build()
// Post a notification to be shown in the status bar.
notificationManager.notify(1, notification)
// Creating media player and starting music - service main task
mediaPlayer = MediaPlayer.create(this, song)
//mediaPlayer.start()
//isPlaying = true
//songDuration = mediaPlayer.duration.toLong()
// Starting Service
startForeground(1, notification)
}
return Service.START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
// Stop playing music and set isPlaying to false
mediaPlayer.stop()
isPlaying = false
// Unregistering broadcast receiver
unregisterReceiver(playerNotificationReceiver)
}
My fragment class:
class PlayerFragment : Fragment() {
private lateinit var playerServiceIntent: Intent
private var isPlaying: Boolean = false
// Animation lazy initalization
private val rotateAnimation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.player_button_rotation) }
// Binding
private var _binding: FragmentPlayerBinding? = null
private val binding
get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// playerServiceIntent
playerServiceIntent = Intent(activity , PlayerService::class.java)
playerServiceIntent.putExtra("song", R.raw.taco_hemingway_europa)
// Starting service
requireActivity().startService(playerServiceIntent)
}
override fun onResume() {
super.onResume()
}
override fun onPause() {
super.onPause()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = FragmentPlayerBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// playerServiceIntent
playerServiceIntent = Intent(activity , PlayerService::class.java)
playerServiceIntent.putExtra("song", R.raw.taco_hemingway_europa)
// Starting service
requireActivity().startService(playerServiceIntent)
binding.playerPlayButton.setOnClickListener {
if(isPlaying){
// Pending intent with broadcast for custom view play button on click
val playIntent = Intent(getString(R.string.PLAY_ACTION))
playIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val backPendingIntent = PendingIntent.getBroadcast(context, 0, playIntent, PendingIntent.FLAG_IMMUTABLE)
binding.playerPlayButton.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.pause_button, null))
} else {
// Pending intent with broadcast for custom view pause button on click
val pauseIntent = Intent(getString(R.string.PAUSE_ACTION))
pauseIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val backPendingIntent = PendingIntent.getBroadcast(context, 0, pauseIntent, PendingIntent.FLAG_IMMUTABLE)
binding.playerPlayButton.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.play_button, null))
// TODO Start animation
//binding.playerPlayButton.startAnimation(rotateAnimation)
}
}
// TODO Time bar implementation
/*
Timer implementation
val timer = Timer()
if(mediaPlayer != null && mediaPlayer.isPlaying){
timer.scheduleAtFixedRate(timerTask {
currentPosition = mediaPlayer.currentPosition.toLong()
binding.textView.text = currentPosition.toString()
},0,1000)
} else {
timer.cancel()
timer.purge()
}
*/
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
A BroadcastReceiver is short lived. You cannot bind to a Service in a BroadcastReceiver, as binding is an asynchronous process and your BroadcastReceiver will be gone before the binding completes.
You can request that the Service do something by calling startService() with an Intent that has some ACTION in it that tells the Service what you want to do.
Or you can just change your notification so that the actions go directly to the Service instead of going via your BroadcastReceiver.
As an alternative, (this is kinda hacky), you could store the MediaPlayer refrerence in a public static (global) variable and then you could use it directly from your BroadcastReceiver. This is not the recommended approach, but it does work.
I have a RecyclerView with a list of customLayout alarms. Each alarm has a switch for on and off.
in my adapter I have a setOnCheckedChangeListener that checks if the switch has been manually turned on or off through the HomeFragment and then calls the AlarmManager to cancel or schedule an alarm.
But when an alarm is called after the broadcast is received, I then need the switch to be turned off automatically ( assuming the alarm isn't repeating)
How can I go about this?
Adapter:
val switch = holder.itemView.findViewById<Switch>(R.id.switch_alarm)
switch.setOnCheckedChangeListener { _, isChecked ->
if (onItemClickListener != null) {
if (isChecked) {
onItemClickListener?.setSwitchOn(currentItem)
} else {
onItemClickListener?.setSwitchOff(currentItem)
}
}
}
Home Fragment:
override fun setSwitchOn(alarm: Alarm) {
val toastTime = if (alarm.minute > 9) {
"${alarm.hour}:${alarm.minute}"
} else {
"${alarm.hour}:0${alarm.minute}"
}
val alarmManager = AlarmManager(
alarm.id,
alarm.hour,
alarm.minute,
true,
alarm.repeat,
)
alarmManager.cancel(requireContext())
Toast.makeText(context, "Alarm set for $toastTime", Toast.LENGTH_SHORT).show()
}
override fun setSwitchOff(alarm: Alarm) {
val alarmManager = AlarmManager(
alarm.id,
alarm.hour,
alarm.minute,
true,
alarm.repeat,
)
alarmManager.cancel(requireContext())
Toast.makeText(context, "Alarm cancelled", Toast.LENGTH_SHORT).show()
}
Receiver:
override fun onReceive(context: Context?, intent: Intent?) {
val intent = Intent(context, AlarmRingActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context!!.startActivity(intent)
}
}
I am trying to download a video for offline playing in exoplayer, but I don't know how to listen for onDownloadComplete. In the exoplayer docs they say DownloadService is a wrap around android DownloadManager so I try to listen for DownloadManager.ACTION_DOWNLOAD_COMPLETE broadcast but it's not working, actually this is my first time using exoplayer.
Download Service
class MediaDownloadService : DownloadService(
C.DOWNLOAD_NOTIFICATION_ID, DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL,
C.CHANNEL_ID, R.string.channel_name, R.string.channel_description
) {
override fun onCreate() {
registerReceiver(onComplete, IntentFilter(ACTION_DOWNLOAD_COMPLETE))
super.onCreate()
}
override fun onDestroy() {
unregisterReceiver(onComplete)
super.onDestroy()
}
override fun getDownloadManager(): DownloadManager {
return DownloadUtil.getDownloadManager(this)
}
override fun getForegroundNotification(downloads: MutableList<Download>): Notification {
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val notificationHelper = DownloadNotificationHelper(this, C.CHANNEL_ID)
return notificationHelper.buildProgressNotification(
R.drawable.ic_notification,
pendingIntent,
"simple message",
downloads
)
}
override fun getScheduler(): Scheduler? {
return null
}
val onComplete: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(ctxt: Context?, intent: Intent?) {
toast("Download COmpleted")
}
}
}
You can compare bytesDownloaded and contentLength to check if it's finish downloading.
downloadManager.addListener(object : DownloadManager.Listener {
override fun onDownloadChanged(downloadManager: DownloadManager, download: Download) {
if (download.bytesDownloaded == download.contentLength) {
Log.d("Completed")
}
}
})
I want to show FCM received message in my mainActivity as a bottomNavigation badge,
but how can I send data to activity from services?
I used:
override fun onMessageReceived(p0: RemoteMessage) {
super.onMessageReceived(p0)
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.putExtra("badge", p0.notification?.body)
startActivity(intent)
}
but this code open new MainActivity, while there's another one!
You could try sending a LocalBroadcast if your service and activity run in the same process.
// Inside the service
override fun onMessageReceived(p0: RemoteMessage) {
super.onMessageReceived(p0)
val lbm = LocalBroadcastManager.getInstance(this)
val dataIntent = Intent().apply {
putExtra("badge", p0.notification?.body)
}
lbm.sendBroadcast(dataIntent)
}
// Inside the activity
private val lbm by lazy { LocalBroadcastManager.getInstance(this) }
private val badgeListener = object : BroadcastReceiver() {
override fun onReceive(ctx: Context, data: Intent) {
val count = intent.getIntExtra("badge")
// Update the view here
}
}
override fun onCreate(){
// Other stuff
lbm.registerReceiver(badgeListener)
}
override fun onDestroy() {
lbm.unregisterReceiver(badgeListener)
}
I want to use my BroadcastReceiver as sender of data into my activity. For this reason I'm using LocalBroadcastManager. This manager is used to register and unregister my receiver. Problem is that Context in onReceive method is different than Context in onStart and onStop method.
I need to pass activity context into my BroadcastReceiver or instance of LocalBroadcastManager initialized inside Activity. Because my receiver is not receiving any data.
Maybe it is not fault of this manager context but I don't know why it doesnt work since I implemented this manager.
class GPSReceiver: BroadcastReceiver(){
companion object{
const val GPS_PAYLOAD = "gps_payload"
}
override fun onReceive(context: Context, intent: Intent) {
try {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val int = Intent(GPS_PAYLOAD)
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
int.putExtra(GPS_PAYLOAD, true)
} else {
int.putExtra(GPS_PAYLOAD, false)
}
LocalBroadcastManager.getInstance(context).sendBroadcast(int)
} catch (ex: Exception) {
}
}
}
Registering receiver inside Activity:
private val gpsStatusReceiver = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
App.log("isGpsEnabled: onReceive")
val gpsStatus = intent?.extras?.getBoolean(GPS_PAYLOAD)
if (gpsStatus != null) {
if (gpsStatus){
App.log("isGpsEnabled: true")
hideGpsSnackbar()
} else {
App.log("isGpsEnabled: false")
showGpsSnackbar()
}
} else {
App.log("isGpsEnabled: null")
}
}
}
override fun onStart() {
super.onStart()
LocalBroadcastManager.getInstance(this).apply {
val filter = IntentFilter()
filter.apply {
addAction("android.location.PROVIDERS_CHANGED")
addAction(GPS_PAYLOAD)
}
registerReceiver(gpsStatusReceiver, filter)
}
}
I have seen your code. So there is not issue with context, but in the approach.
Your are registering your reciever with the same strings in which you are getting you data inside the Reciever.
So Send Your broadcast from Fragment/Activity
Send BroadCast Like
private fun sendSuccessfulCheckoutEvent() {
val intent = Intent("successful_checkout_event")
intent.putExtra("cartID", cartId)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
And Listen it in Activity/Fragment like this
1) Create broadcast Reciever
private val checkoutDoneReciever : BroadcastReceiver = object : BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
val cartNumbers = intent.getIntExtra("cartID", 0)
Log.d("receiver", "Got message: $cartNumbers.toString()")
}
}
2) Register it in onCreate()/onStart()
LocalBroadcastManager.getInstance(this).registerReceiver(cartUpdatedReceiver,IntentFilter("successful_checkout_event"))
3) Unregister it in onDestroy()
LocalBroadcastManager.getInstance(this).unregisterReceiver(cartUpdatedReceiver)