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")
}
}
})
Related
I have a foreground service. It does some async work in the background and periodically issues a notification asking the user if the work should be stopped.
The notification has a button "Yes, please" and when clicked it must invoke stopAction method.
The code below is where I'm stuck. I'm maybe way off and this can't be done. Any advice?
MainService.kt
...
override fun onCreate() {
subscribeToStopActionRequest()
}
private fun subscribeToStopActionRequest () {
var eventReceiverHelper = EventReceiverHelper { stopAction() }
val filter = IntentFilter().apply {
addAction("${packageName}.stop_action_request")
}
registerReceiver(eventReceiverHelper, filter)
}
private fun stopAction () {
...
}
private fun showNotification () {
val intent = Intent(this, EventService::class.java)
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
var notification = NotificationCompat.Builder(this, state.notificationChannelId)
.setContentTitle("Want to stop?")
.addAction(R.drawable.stop_icon, "Yes, please", pendingIntent)
.build()
with(NotificationManagerCompat.from(this)) {
notify(1, notification)
}
}
Event receiver helper
class EventReceiverHelper(val cb: () -> Unit): BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
cb()
}
}
Define a constant:
private const val EXTRA_STOP = "stop";
Then create an intent for your service and put an extra flag:
val intent = Intent(context, YourService::class.java);
intent.putExtra(EXTRA_STOP, true);
Now you can create a pending intent as your handler:
val pendingIntent: PendingIntent = PendingIntent.getService(context, YOUR_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE)
This pending intent will trigger the onStartCommand method on your service, where you can check whether the stop flag was set.
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null && intent.getBooleanExtra(EXTRA_STOP, false)) {
stopAction()
}
...
}
I'm trying to run exoplayer in a foreground service (not a MediaBrowserServiceCompat).
Here is my service -
#AndroidEntryPoint
class PodcastPlayerService: Service() {
#Inject
lateinit var dataSourceFactory: DefaultDataSourceFactory
#Inject
lateinit var exoPlayer: SimpleExoPlayer
lateinit var podcastNotificationManager: PodcastNotificationManager
lateinit var podcast: Podcast
override fun onDestroy() {
super.onDestroy()
exoPlayer.release()
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
exoPlayer.stop()
exoPlayer.release()
}
override fun onBind(p0: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("Pit stop", "2")
val b = intent!!.getBundleExtra("test")
if (b != null) {
Log.i("Pit stop", "3")
podcast = b.getParcelable<Podcast>(ArgumentKeyAndValues.KEY_PODCAST)!!
}
val mediaSource = buildMediaSource(Uri.parse("https://something.etc/file.mp3"))
if (mediaSource != null) {
exoPlayer.prepare(mediaSource)
exoPlayer.playWhenReady = true
podcastNotificationManager =
PodcastNotificationManager(
this,
PodcastPlayerNotificationListener(this)
)
podcastNotificationManager.showNotification(exoPlayer)
}
return START_STICKY
}
private fun buildMediaSource(uri: Uri): MediaSource? {
return ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(uri)
}
}
Notification Listener -
class PodcastPlayerNotificationListener(private val podcastPlayerService: PodcastPlayerService):
PlayerNotificationManager.NotificationListener {
override fun onNotificationPosted(
notificationId: Int,
notification: Notification,
ongoing: Boolean) {
super.onNotificationPosted(notificationId, notification, ongoing)
podcastPlayerService.apply {
if(ongoing) {
ContextCompat.startForegroundService(this,
Intent(applicationContext, this::class.java))
startForeground(OtherConstants.PODCAST_NOTIFICATION_ID, notification)
}
}
}
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
super.onNotificationCancelled(notificationId, dismissedByUser)
podcastPlayerService.apply {
stopForeground(true)
stopSelf()
}
}
}
Podcast Notification Manager -
class PodcastNotificationManager(private val context: Context,
notificationListener: PlayerNotificationManager.NotificationListener) {
private val notificationManager: PlayerNotificationManager
init {
notificationManager = PlayerNotificationManager.createWithNotificationChannel(
context,
OtherConstants.NOTIFICATION_CHANNEL_ID,
R.string.notification_channel_name,
R.string.notification_channel_description,
OtherConstants.PODCAST_NOTIFICATION_ID,
DescriptionAdapter(),
notificationListener
).apply {
setSmallIcon(R.drawable.exo_icon_play)
}
}
fun showNotification(player: Player) {
notificationManager.setPlayer(player)
}
private inner class DescriptionAdapter : PlayerNotificationManager.MediaDescriptionAdapter {
override fun getCurrentContentTitle(player: Player): String {
val window = player.currentWindowIndex
return "Title"
}
override fun getCurrentContentText(player: Player): String? {
val window = player.currentWindowIndex
return "Description"
}
override fun getCurrentLargeIcon(
player: Player,
callback: BitmapCallback
): Bitmap? = null
override fun createCurrentContentIntent(player: Player): PendingIntent? {
val window = player.currentWindowIndex
return null
}
}
}
Here is how I start the service -
val intent = Intent(context, PodcastPlayerService::class.java)
val serviceBundle = Bundle()
serviceBundle.putParcelable("test", podcast)
intent.putExtra(ArgumentKeyAndValues.KEY_PODCAST, serviceBundle)
context?.let { Util.startForegroundService(it, intent) }
However, when I do this onStartCommand keeps getting called (I'm assuming that the OS keeps killing my service for some reason and START_STICKY forces it to start again) and nothing happens.
If I place the media, notification manager and listener code in onCreate the service works fine.
Where am I going wrong?
Turns out I was starting the foreground service twice -
ContextCompat.startForegroundService(this,
Intent(applicationContext, this::class.java))
startForeground(OtherConstants.PODCAST_NOTIFICATION_ID, notification)
I have a service which started by on booted completed event it, but the app crashes with the error message as in above. Please help on how can I start my Service on BroadCast receiver event of Boot_Completed.
MyService.kt
class MyService : Service() {
override fun onCreate() {
Log.d(TAG, "onCreate")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onDestroy() {
Log.d(TAG, "DO SOME STAFF")
}
}
MyBroadCaster.kt
class StartRelayServiceAtBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
val serviceIntent = Intent(context, MyService::class.java)
context.startService(serviceIntent)
}
}
}
Upon some searching I got the answer that I had to check the SDK version that I can then start it as foreground service or just with starteService;
class StartRelayServiceAtBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
val intent = Intent(context, MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
Log.i("Autostart", "started")
}
}
}
There are limitations on apps in the background. Obviously, if the device just booted, all apps are "in the background". You cannot start a Service from a background app. You probably need to use JobScheduler to to what you want.
See this document for a discussion about the limitations on background apps and how to migrate to other solutions that are allowed:
https://developer.android.com/about/versions/oreo/background
Since Agora works by creating a channel how can one show the other user that there is a call for .
One way I thought was by creating a service that will listen to the server using retrofit and if there is a change in status of the call I will show it to him but in android background services are restricted.
Another way I thought was by creating a broadcast receiver with a scheduler. Can Any One Help in this.
class CallReciever : BroadcastReceiver() {
val REQUEST_CODE : Int= 12345
val ACTION :String= "com.codepath.example.servicesdemo.alarm"
override fun onReceive(context: Context, intent: Intent) {
val i = Intent(context, MyTestService::class.java)
startWakefulService(context, i)
}
}
class MyTestService : IntentService("MyTestService") {
override fun onHandleIntent(intent: Intent?) {
// Do the task here
getBanner()
}
private fun getBanner() {
val service = RetrofitCall.provideRetrofit().create(callrecieveAPI::class.java)
val call = service.banner()
call.enqueue(object : Callback<CallReceivePOJO> {
override fun onResponse(call: Call<CallReceivePOJO>, response: Response<CallReceivePOJO>) {
//showFailureDialog(GuestCheckoutActivity.this, response.body().getMessage());
CommonObjects.channelid =response.body()!!.data.toString()
}
override fun onFailure(call: Call<CallReceivePOJO>, t: Throwable) {
// handle execution failures like no internet connectivity
}
})
}
}
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
scheduleAlarm()
object : CountDownTimer(5000, 1000) {
override fun onFinish() {
intent = Intent(applicationContext, VideoChatViewActivity::class.java)
startActivity(intent)
}
override fun onTick(p0: Long) {}
}.start()
}
fun scheduleAlarm() {
// Construct an intent that will execute the AlarmReceiver
val intent = Intent(applicationContext, CallReciever::class.java)
// Create a PendingIntent to be triggered when the alarm goes off
val pIntent = PendingIntent.getBroadcast(
this, REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT
)
// Setup periodic alarm every every half hour from this point onwards
val firstMillis = System.currentTimeMillis() // alarm is set right away
val alarm = this.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// First parameter is the type: ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC_WAKEUP
// Interval can be INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_DAY
alarm.setInexactRepeating(
AlarmManager.RTC_WAKEUP, firstMillis,
10000, pIntent
)
}
Agora has a few sample apps that show how to achieve a "call" functionality using Agora's Video and Real-Time Messaging SDKs along with the native APIs (Android's ConnectionService and iOS's CallKit).
Android: https://github.com/AgoraIO-Usecase/Video-Calling/tree/master/OpenDuo-Android
IOS: https://github.com/AgoraIO-Usecase/Video-Calling/tree/master/OpenDuo-iOS
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)
}