I am developing an audio player app that works well but I am facing one problem if I minimize the app it kill my foreground service. I don't know why it's happening can anyone suggest me any solution
MainActivity
import android.Manifest
import android.app.PendingIntent
import android.content.*
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat
import android.text.Html
import android.util.Log
import android.view.WindowManager
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.brahmakumaris.fragment.*
import com.brahmakumaris.model.getDashboard.RecentSong
import com.brahmakumaris.service.MusicPlayerService
import com.brahmakumaris.service.MyService
import com.brahmakumaris.util.*
import com.google.android.exoplayer2.DefaultLoadControl
import com.google.android.exoplayer2.ExoPlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.ui.PlayerNotificationManager
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*
import kotlin.collections.ArrayList
class MainActivity : AppCompatActivity(), OnFragmentInteractionListener {
override fun onFragmentInteraction(screen: String, model: RecentSong, songList: ArrayList<RecentSong>, position: Int) {
MyLog.e(TAG, screen)
when (screen) {
getString(R.string.speakers) -> {
bottom_navigation.selectedItemId = R.id.nav_speaker
supportFragmentManager.switch(
newFrag = SpeakerPageFragment.newInstance(),
tag = getString(R.string.speakers)
)
}
getString(R.string.classes) -> {
bottom_navigation.selectedItemId = R.id.nav_class
supportFragmentManager.switch(
newFrag = ClassesPageFragment.newInstance(),
tag = getString(R.string.classes)
)
}
getString(R.string.songs) -> {
bottom_navigation.selectedItemId = R.id.nav_song
supportFragmentManager.switch(
newFrag = SongsPageFragment.newInstance(),
tag = getString(R.string.songs)
)
}
getString(R.string.play_song) -> {
if(!model.name.isNullOrBlank()) {
songPosition = position
mSongList = songList
MyLog.e(TAG, "======= ${getString(R.string.play_song)} ${model.name}")
isPlaying = false // to start new song
// play song
if (!lnPlayer.isVisible) {
lnPlayer.isVisible = true
}
titleTxt.setHtmlText(model.name)
if(mBound) {
startService()
mMusicPlayerService.playSong(mSongList,songPosition)
media_button.setImageResource(R.drawable.ic_pause_black_24dp)
}
setPlayPause(!mMusicPlayerService.isPlaying())
initSeekBar()
}
}
else -> {
}
}
}
private val TAG = javaClass.simpleName
private var isPlaying = false
private var mediaSource: ProgressiveMediaSource? = null
// notification
private lateinit var mediaSessionConnector: MediaSessionConnector
private lateinit var mediaSession: MediaSessionCompat
private lateinit var playerNotificationManager: PlayerNotificationManager
private var handler: Handler? = null
private val dashUrl = "http://www.panacherock.com/downloads/mp3/01_All_Tangled_Up.mp3"
var mSongList: ArrayList<RecentSong> = ArrayList()
var songPosition = 0
val MESSAGE_KEY = "message_key"
private lateinit var mMusicPlayerService:MusicPlayerService
private lateinit var mPlayer: SimpleExoPlayer
private var mBound = false
private val mServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
val binder = iBinder as MusicPlayerService.MyServiceBinder
mMusicPlayerService = binder.getService()
mPlayer = mMusicPlayerService.getPlayerInstance()!!
mPlayer.addListener(playerListener)
mBound = true
MyLog.e(TAG,"onServiceConnected: ")
}
override fun onServiceDisconnected(componentName: ComponentName) {
mBound = false
MyLog.e(TAG,"onServiceDisconnected: ") // calles only in rare case if service destroy unexpectedly
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
if (savedInstanceState == null) {
supportFragmentManager.switch(
newFrag = DashboardPageFragment.newInstance(),
tag = getString(R.string.home)
)
}
getIntentData()
bottom_navigation.setOnNavigationItemSelectedListener { item ->
hideKeybord()
when(item.itemId) {
R.id.nav_home -> {
checkFragment()
supportFragmentManager.switch(
newFrag = DashboardPageFragment.newInstance(),
tag = getString(R.string.home)
)
true
}
R.id.nav_song -> {
checkFragment()
supportFragmentManager.switch(
newFrag = SongsPageFragment.newInstance(),
tag = getString(R.string.songs)
)
true
}
R.id.nav_speaker -> {
checkFragment()
supportFragmentManager.switch(
newFrag = SpeakerPageFragment.newInstance(),
tag = getString(R.string.speakers)
)
true
}
R.id.nav_class -> {
checkFragment()
supportFragmentManager.switch(
newFrag = ClassesPageFragment.newInstance(),
tag = getString(R.string.classes)
)
true
}
R.id.nav_search -> {
checkFragment()
supportFragmentManager.switch(
newFrag = SearchPageFragment.newInstance(),
tag = getString(R.string.search)
)
true
}
else -> false
}
}
bottom_navigation.setOnNavigationItemReselectedListener { } //disable reselection tab
media_button.setOnClickListener {
// startService()
setPlayPause(!isPlaying)
}
imgClose.setOnClickListener {
isPlaying = true // to stop song
playerNotificationManager.setPlayer(null)
setPlayPause(!isPlaying)
if (lnPlayer.isVisible) {
lnPlayer.isVisible = false
}
}
}
private fun getIntentData() {
if (intent!=null && intent.hasExtra("internet") && !intent.getBooleanExtra("internet", false)) {
this.changeFragment(
DownloadPageFragment.newInstance(), Constants.DOWNLOAD_TAG
)
}
}
private fun checkFragment() {
val fragment = getSupportFragmentManager().findFragmentByTag(Constants.SUB_CATEGORY_TAG)
if (fragment != null) {
getSupportFragmentManager().beginTransaction().remove(fragment).commit()
}
val fragment2 = getSupportFragmentManager().findFragmentByTag(Constants.DOWNLOAD_TAG)
if (fragment2 != null) {
getSupportFragmentManager().beginTransaction().remove(fragment2).commit()
}
}
private val playerListener by lazy {
object : Player.EventListener {
override fun onPlayerError(error: ExoPlaybackException) {
super.onPlayerError(error)
//onError(error)
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
super.onPlayerStateChanged(playWhenReady, playbackState)
when (playbackState) {
Player.STATE_BUFFERING -> Log.e(TAG,"STATE_BUFFERING")
Player.STATE_ENDED -> Log.e(TAG,"STATE_ENDED")
Player.STATE_IDLE -> Log.e(TAG,"STATE_IDLE")
Player.STATE_READY -> {
// setPlayPause(playWhenReady)
isPlaying = playWhenReady
mPlayer.setPlayWhenReady(playWhenReady)
if (!isPlaying) {
media_button.setImageResource(R.drawable.ic_play_arrow_black_24dp)
} else {
setProgress()
media_button.setImageResource(R.drawable.ic_pause_black_24dp)
}
if (playWhenReady) {
Log.e(TAG, "PlaybackStatus.PLAYING")
} else {
Log.e(TAG, "PlaybackStatus.PAUSED")
}
titleTxt.setHtmlText(mSongList[mPlayer.currentWindowIndex].name)
MyLog.e(TAG, "====== " + mSongList[mPlayer.currentWindowIndex].name)
MyLog.e(TAG, "======111 " + mSongList[mPlayer.currentWindowIndex].descripation)
}
else -> Log.e(TAG, "PlaybackStatus.IDLE")
}
}
}
}
private fun startService() {
val myService = Intent(this, MusicPlayerService ::class.java)
Util.startForegroundService(this,myService)
}
private fun stopService() {
val myService = Intent(this, MusicPlayerService ::class.java)
stopService(myService)
}
private fun setPlayPause(play: Boolean) {
if(mBound) {
if(mMusicPlayerService.isPlaying()) {
mMusicPlayerService.pause()
media_button.setImageResource(R.drawable.ic_play_arrow_black_24dp)
} else {
startService()
playerNotificationManager = mMusicPlayerService.getNotificationInstance()!!
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}*/
mMusicPlayerService.play()
setProgress()
media_button.setImageResource(R.drawable.ic_pause_black_24dp)
}
}
/*isPlaying = play
exoPlayer.setPlayWhenReady(play)
if (!isPlaying) {
media_button.setImageResource(R.drawable.ic_play_arrow_black_24dp)
} else {
setProgress()
media_button.setImageResource(R.drawable.ic_pause_black_24dp)
}*/
}
private fun stringForTime(timeMs: Int): String {
val mFormatBuilder: StringBuilder
val mFormatter: Formatter
mFormatBuilder = StringBuilder()
mFormatter = Formatter(mFormatBuilder, Locale.getDefault())
val totalSeconds = timeMs / 1000
val seconds = totalSeconds % 60
val minutes = totalSeconds / 60 % 60
val hours = totalSeconds / 3600
mFormatBuilder.setLength(0)
return if (hours > 0) {
mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString()
} else {
mFormatter.format("%02d:%02d", minutes, seconds).toString()
}
}
private fun setProgress() {
seekPlayerProgress.progress = 0
seekPlayerProgress.max = mPlayer.getDuration().toInt() / 1000
position.setText(stringForTime(mPlayer.getCurrentPosition().toInt()))
duration.setText(stringForTime(mPlayer.getDuration().toInt()))
if (handler == null) handler = Handler()
//Make sure you update Seekbar on UI thread
handler!!.post(object : Runnable {
override fun run() {
if (mPlayer != null && ::mPlayer.isInitialized && isPlaying) {
seekPlayerProgress.max = mPlayer.getDuration().toInt() / 1000
val mCurrentPosition = mPlayer.getCurrentPosition().toInt() / 1000
seekPlayerProgress.progress = mCurrentPosition
position.setText(stringForTime(mPlayer.getCurrentPosition().toInt()))
duration.setText(stringForTime(mPlayer.getDuration().toInt()))
handler!!.postDelayed(this, 1000)
}
}
})
}
private fun initSeekBar() {
seekPlayerProgress.requestFocus()
seekPlayerProgress.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (!fromUser) {
// We're not interested in programmatically generated changes to
// the progress bar's position.
return
}
mPlayer.seekTo((progress * 1000).toLong())
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
}
})
seekPlayerProgress.setMax(0)
seekPlayerProgress.setMax(mPlayer.getDuration().toInt() / 1000)
}
private var doubleBackToExitPressedOnce = false
override fun onBackPressed() {
val fragment = getSupportFragmentManager().findFragmentByTag(Constants.SUB_CATEGORY_TAG)
if (fragment != null) {
getSupportFragmentManager().beginTransaction().remove(fragment).commit()
return
}
val fragment2 = getSupportFragmentManager().findFragmentByTag(Constants.DOWNLOAD_TAG)
if (fragment2 != null) {
getSupportFragmentManager().beginTransaction().remove(fragment2).commit()
return
}
if (doubleBackToExitPressedOnce) {
super.onBackPressed()
return
}
this.doubleBackToExitPressedOnce = true
this.showSnackBarToast("Please click BACK again to exit")
Handler().postDelayed(Runnable { doubleBackToExitPressedOnce = false }, 2000)
}
override fun onDestroy() {
if(::playerNotificationManager.isInitialized) {
playerNotificationManager.setPlayer(null)
}
killPlayer()
// stopService()
MyLog.e(TAG," #### onDestroy #### ")
super.onDestroy()
}
override fun onStop() {
super.onStop()
if(mBound) {
unbindService(mServiceConnection)
mBound = false
}
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(broadCastReceiver)
MyLog.e(TAG," #### onStop #### ")
}
private fun killPlayer() {
if (mPlayer != null) {
mPlayer.release()
mediaSource = null
}
MyLog.e(TAG,"#### killPlayer ####")
}
private fun getHtmlText(str:String):String {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return (Html.fromHtml(str, Html.FROM_HTML_MODE_COMPACT)).toString()
} else {
return (Html.fromHtml(str)).toString()
}
}
private fun setMediaSession() {
mediaSession = MediaSessionCompat(this#MainActivity,"MEDIA_SESSION_TAG")
mediaSession.isActive = true
playerNotificationManager.setMediaSessionToken(mediaSession.sessionToken) //enhance media stye notification and provide artwork in lock screen
mediaSessionConnector = MediaSessionConnector(mediaSession)
//timeline is the internal representation of the pllaylist after the player has been prepared
mediaSessionConnector.setQueueNavigator(object : TimelineQueueNavigator(mediaSession){
override fun getMediaDescription(player: Player?, windowIndex: Int): MediaDescriptionCompat {
return getMediaDescriptionData(this#MainActivity, mSongList[windowIndex])
}
})
mediaSessionConnector.setPlayer(exoPlayer/*, null*/) //sync player wth media session
}
fun getMediaDescriptionData(context: Context, sample: RecentSong): MediaDescriptionCompat {
val extras = Bundle()
val options = BitmapFactory.Options()
options.inSampleSize = 8
// val bitmap = BitmapFactory.decodeFile(getBitmap(context, sample.bitmapResource), options)
val bitmap = context.getBitmap(R.drawable.bg)
extras.putParcelable(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
extras.putParcelable(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, bitmap)
return MediaDescriptionCompat.Builder()
.setMediaId(sample.song)
.setIconBitmap(bitmap)
.setTitle(sample.name)
.setDescription(sample.descripation)
.setExtras(extras)
.build()
}
override fun onStart() {
super.onStart()
bindService(Intent(this,MusicPlayerService::class.java),mServiceConnection,Context.BIND_AUTO_CREATE)
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadCastReceiver, IntentFilter(MusicPlayerService().MUSIC_COMPLETED))
}
val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
MyLog.e(TAG,"onReceive: ${intent?.getBooleanExtra(MESSAGE_KEY,false)}")
if(intent!!.getBooleanExtra(MESSAGE_KEY,false)) {
setPlayPause(intent.getBooleanExtra(MESSAGE_KEY,false))
if (intent.getBooleanExtra(MESSAGE_KEY,false)) {
Log.e(TAG, "PlaybackStatus.PLAYING")
} else {
Log.e(TAG, "PlaybackStatus.PAUSED")
}
titleTxt.setHtmlText(mSongList[mPlayer.currentWindowIndex].name)
MyLog.e(TAG,"====== "+mSongList[mPlayer.currentWindowIndex].name)
MyLog.e(TAG,"======111 "+mSongList[mPlayer.currentWindowIndex].descripation)
}
}
}
}
MusicPlayerService
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Binder
import android.os.IBinder
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.brahmakumaris.MainActivity
import com.brahmakumaris.R
import com.brahmakumaris.model.getDashboard.RecentSong
import com.brahmakumaris.util.MyLog
import com.brahmakumaris.util.getBitmap
import com.brahmakumaris.util.getPlaintextfromHtmlHtmlText
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.ui.PlayerNotificationManager
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
class MusicPlayerService : Service() {
private val TAG = javaClass.simpleName
private val mContext: Context = this
private val mBinder = MyServiceBinder()
val MUSIC_COMPLETED = "music completed"
val mPlayer: SimpleExoPlayer by lazy { SimpleExoPlayer.Builder(this).build() }
private lateinit var playerNotificationManager: PlayerNotificationManager
var mSongList: ArrayList<RecentSong> = ArrayList()
override fun onCreate() {
super.onCreate()
MyLog.d(TAG,"onCreate: ")
}
fun playSong(mSongList: ArrayList<RecentSong> = ArrayList(), position: Int = 0)/* mContext: Context = this*/ {
this.mSongList = mSongList
val dataSourceFactory = DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext,getString(R.string.app_name)))
val concateMediaSource = ConcatenatingMediaSource()
for (i in mSongList) {
val mediaSource = ProgressiveMediaSource
.Factory(
DefaultDataSourceFactory(mContext, dataSourceFactory),
DefaultExtractorsFactory()
)
.createMediaSource(/*i.uri*/Uri.parse(i.musicFile))
concateMediaSource.addMediaSource(mediaSource)
}
mPlayer.prepare(concateMediaSource)
mPlayer.seekToDefaultPosition(position)
mPlayer.playWhenReady =true
setNotification()
}
private fun setNotification() {
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
this,"channel_id",R.string.channelName,R.string.channelDescription,11,
object : PlayerNotificationManager.MediaDescriptionAdapter{
override fun createCurrentContentIntent(player: Player): PendingIntent? {
val intent = Intent(mContext, MainActivity::class.java)
return PendingIntent.getActivity(mContext,0,intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
override fun getCurrentContentText(player: Player): String? {
// return mSongList[player.currentWindowIndex].descripation // descrption
return mSongList[player.currentWindowIndex].descripation.getPlaintextfromHtmlHtmlText() // descrption
}
override fun getCurrentContentTitle(player: Player): String {
// return mSongList[player.currentWindowIndex].name // title
return mSongList[player.currentWindowIndex].name.getPlaintextfromHtmlHtmlText()
}
override fun getCurrentLargeIcon(
player: Player,
callback: PlayerNotificationManager.BitmapCallback
): Bitmap? {
return mContext.getBitmap(R.drawable.bg)
}
} ,object : PlayerNotificationManager.NotificationListener {
override fun onNotificationStarted(notificationId: Int, notification: Notification) {
startForeground(notificationId,notification)
}
override fun onNotificationPosted(notificationId: Int, notification: Notification, ongoing: Boolean) {
startForeground(notificationId,notification)
}
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
stopSelf()
}
override fun onNotificationCancelled(notificationId: Int) {
stopSelf()
}
}
)
//show hide button
playerNotificationManager.setUseStopAction(false) //stop song
playerNotificationManager.setRewindIncrementMs(0) //hide rewind button
playerNotificationManager.setFastForwardIncrementMs(0) //hide fast forward button
playerNotificationManager.setPlayer(mPlayer)
}
inner class MyServiceBinder : Binder() {
fun getService(): MusicPlayerService = this#MusicPlayerService
}
fun getPlayerInstance(): SimpleExoPlayer? {
return mPlayer
}
fun getNotificationInstance(): PlayerNotificationManager? {
return playerNotificationManager
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
MyLog.d(TAG,"onStartCommand: ")
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
MyLog.d(TAG,"onBind: ")
return mBinder
}
override fun onUnbind(intent: Intent?): Boolean {
MyLog.d(TAG,"onUnbind: ")
return true
}
override fun onRebind(intent: Intent?) {
MyLog.d(TAG,"onRebind: ")
super.onRebind(intent)
}
override fun onDestroy() {
MyLog.e(TAG,"onDestroy: ")
super.onDestroy()
mPlayer.release()
if(::playerNotificationManager.isInitialized) {
playerNotificationManager.setPlayer(null)
}
}
// public client method
fun isPlaying():Boolean {
return mPlayer.isPlaying
}
fun play() {
mPlayer.setPlayWhenReady(true)
}
fun pause() {
mPlayer.setPlayWhenReady(false)
}
}
if I minimize the app then it's printing unbind and then it directly going to onDestroy why it's happening can anyone help me,
Any Help Would Be Highly Appreciated.
I just remove onStop unbind and unregister code and set in onDestroy and it's working for me.
You should be using startForeground() with a notification, to Android service that never stops running.
startForeground(1, notification)
Please have a look at this.
https://robertohuertas.com/2019/06/29/android_foreground_services/
I hope this will help you.
I experienced this issue because I set the player on notification manager to null on onStop which causes onNotificationCancelled to be called, you should check, if the notification was canceled by the user then you should stop the service, else leave it running.
override fun onNotificationCancelled(notificationId: Int, dismissedByUser:Boolean)
{
if (dismissedByUser) {
stopSelf()
}
}
Related
I want to use service in jetpack compose. I am trying to bind services in compose app but it always return null to me. I tried this stack overflow. But it didn't work to me. Anyone guide me on this ?
import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Binder
import android.os.IBinder
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
#Composable
inline fun <reified BoundService : Service, reified BoundServiceBinder : Binder> rememberBoundLocalService(
crossinline getService: #DisallowComposableCalls BoundServiceBinder.() -> BoundService,
): BoundService? {
val context: Context = LocalContext.current
var boundService: BoundService? by remember(context) { mutableStateOf(null) }
val serviceConnection: ServiceConnection = remember(context) {
object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
boundService = (service as BoundServiceBinder).getService()
}
override fun onServiceDisconnected(arg0: ComponentName) {
boundService = null
}
}
}
DisposableEffect(context, serviceConnection) {
context.bindService(Intent(context, BoundService::class.java), serviceConnection, Context.BIND_AUTO_CREATE)
onDispose { context.unbindService(serviceConnection) }
}
return boundService
}
BluetoothService.kt
class BluetoothService : Service() {
companion object {
const val ACTION_GATT_CONNECTED =
"com.abc.app.ACTION_GATT_CONNECTED"
const val ACTION_GATT_DISCONNECTED =
"com.abc.app.ACTION_GATT_DISCONNECTED"
private const val STATE_DISCONNECTED = 0
private const val STATE_CONNECTED = 2
}
private var connectionState = STATE_DISCONNECTED
private var bluetoothAdapter: BluetoothAdapter? = null
private val binder = LocalBinder()
private var bluetoothGatt: BluetoothGatt? = null
private val bluetoothGattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
connectionState = STATE_CONNECTED
broadcastUpdate(ACTION_GATT_CONNECTED)
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
connectionState = STATE_DISCONNECTED
broadcastUpdate(ACTION_GATT_DISCONNECTED)
}
}
}
override fun onBind(intent: Intent): IBinder {
return binder
}
inner class LocalBinder : Binder() {
fun getService(): BluetoothService {
return this#BluetoothService
}
}
fun initialize(adapter: BluetoothAdapter?): Boolean {
bluetoothAdapter = adapter
if (bluetoothAdapter == null) {
logE(">> Unable to obtain a BluetoothAdapter.")
return false
}
return true
}
fun connect(address: String): Boolean {
bluetoothAdapter?.let { adapter ->
try {
val device = adapter.getRemoteDevice(address)
// connect to the GATT server on the device
bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
return true
} catch (exception: IllegalArgumentException) {
logE("Device not found with provided address.")
return false
}
} ?: run {
logE("BluetoothAdapter not initialized")
return false
}
}
override fun onUnbind(intent: Intent?): Boolean {
close()
return super.onUnbind(intent)
}
private fun close() {
bluetoothGatt?.let { gatt ->
gatt.close()
bluetoothGatt = null
}
}
private fun broadcastUpdate(action: String) {
val intent = Intent(action)
sendBroadcast(intent)
}
}
I am trying to use bluetoothService it always return null when I check in the condition
BluetoothConnectionContentStateful.kt
#OptIn(ExperimentalLifecycleComposeApi::class)
#Composable
fun BluetoothConnectionContentStateful(
context: Context = LocalContext.current,
viewModel: BloodPressurePairViewModel = getViewModel(),
router: Router = get()
) {
val activity = context as ComponentActivity
val bluetoothService = rememberBoundLocalService<BluetoothService, BluetoothService.LocalBinder> { getService() }
val scanDeviceList = viewModel.scanLeDevices.collectAsStateWithLifecycle()
if (bluetoothService != null) {
if (!bluetoothService.initialize(rememberPairScreenState.bluetoothAdapter)) {
activity.logE(">> Unable to initialize Bluetooth")
} else {
activity.logE(">> initialize Bluetooth")
}
}else{
activity.logE(">> bluetoothService null")
}
}
bluetoothService always return null. Any idea about that?
I have integrated mesibo chat and tried to integrate the mesibo push-notification but not receiving the notification. I had set up all the things related push notification and already success integrate with the backend also.
Not sure why i can't receive notification from mesibo related new chat and new call.
This is my code:
package com.project.bucynapp.ui.message
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.mesibo.api.Mesibo.*
import com.mesibo.calls.api.MesiboCall
import com.project.bucynapp.R
import com.project.bucynapp.models.*
import com.project.bucynapp.ui.message.adapter.ChatListAdapter
import com.project.bucynapp.ui.message.presenter.ChatListPresenter
import com.project.bucynapp.ui.message.view.ChatListView
import com.project.bucynapp.utils.*
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_chat_list.*
import kotlinx.android.synthetic.main.app_bar.toolbar
import kotlinx.android.synthetic.main.app_bar_chat_list.*
import kotlinx.android.synthetic.main.loading_view.*
class ChatListActivity : AppCompatActivity(), View.OnClickListener, ChatListView,
MessageListener, ConnectionListener, SyncListener {
private lateinit var presenter: ChatListPresenter
private lateinit var chatListAdapter: ChatListAdapter
private var chatList: MutableList<ChatModel> = mutableListOf()
private val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
private lateinit var readSession: ReadDbSession
private var userMesibo: MesiboAccountData? = null
var userProfile: UserProfile? = null
private var imageProfile: String? = null
private var nameProfile: String? = null
private var userId: Int = 0
private var email: String? = null
companion object {
private const val USER_ID = "user_id"
private const val IMAGE_PROFILE = "img_profile"
private const val NAME_PROFILE = "name_profile"
private val TAG = ChatListActivity::class.java.canonicalName
fun startActivity(
context: Context,
userId: Int?,
imageProfile: String,
nameProfile: String
) {
Intent(context, ChatListActivity::class.java).apply {
putExtra(USER_ID, userId)
putExtra(IMAGE_PROFILE, imageProfile)
putExtra(NAME_PROFILE, nameProfile)
context.startActivity(this)
}
}
}
private val message: String
get() = edtSendChat.text.toString()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_list)
initToolbar()
userMesibo = ApplicationPrefs.get(Constant.MESIBO_USER)
presenter = ChatListPresenter(this)
userId = intent.getIntExtra(USER_ID, 0)
imageProfile = intent.getStringExtra(IMAGE_PROFILE)
nameProfile = intent.getStringExtra(NAME_PROFILE)
chatListAdapter = ChatListAdapter(chatList)
rvChatList.adapter = chatListAdapter
rvChatList.layoutManager = layoutManager
rvChatList.addItemDecoration(DefaultItemDecoration(spacing = 10.dp, includeEdge = true))
tvName.text = nameProfile
Picasso.get().load(imageProfile)
.error(R.drawable.com_facebook_profile_picture_blank_square)
.placeholder(R.drawable.com_facebook_profile_picture_blank_square).into(imgProfile)
presenter.getEmailUser(userId)
}
private fun initToolbar() {
setSupportActionBar(toolbar)
val backArrow = ContextCompat.getDrawable(this, R.drawable.ic_back_black)
supportActionBar?.setHomeAsUpIndicator(backArrow)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
supportActionBar?.setDisplayShowTitleEnabled(false)
tvSeeProfile.setOnClickListener(this)
imgCalling.setOnClickListener(this)
imgVideoCall.setOnClickListener(this)
imgSendChat.setOnClickListener(this)
tilChat.setStartIconOnClickListener {
NotMatchActivity.startActivity(this)
}
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
override fun getChatListSuccess(response: MutableList<DataAttributes>) {
}
override fun getChatListFailed(message: String) {
}
override fun successPost(response: SuccessModel?) {
}
override fun failedPost(message: String) {
shortToast(message)
}
override fun onGetEmailUser(data: UserEmailResponse?) {
email = data?.email
startMesibo()
email?.let { loadFromDb(it) }
}
override fun onLoading(isShow: Boolean) {
if (isShow) {
rvChatList.gone()
loadingView.visible()
} else {
rvChatList.visible()
loadingView.gone()
}
}
override fun onNoData(msg: String?) {
shortToast(msg.toString())
}
override fun onBadRequest(msg: String?) {
shortToast(msg.toString())
}
override fun onUnauthorized(msg: String?) {
shortToast(msg.toString())
badToken()
}
override fun onClick(v: View?) {
when (v) {
imgSendChat -> if (message.isNotBlank()) onSendMessage()
tvSeeProfile -> {
UserProfileActivity.startActivity(this, userId)
}
imgCalling -> MesiboCall.getInstance().callUi(this, email, false)
imgVideoCall -> MesiboCall.getInstance().callUi(this, email, true)
}
}
override fun Mesibo_onMessage(p0: MessageParams?, p1: ByteArray?): Boolean {
val type: Int = if (p0?.isIncoming == true) 0 else 1
val msg = String(p1!!)
chatList.add(
ChatModel(
id = p0!!.mid,
msg = msg,
date = p0.ts,
type = type,
status = p0.status
)
)
chatListAdapter.notifyItemInserted(chatList.size)
layoutManager.scrollToPositionWithOffset(chatList.size - 1, 0)
return true
}
override fun Mesibo_onMessageStatus(p0: MessageParams?) {
chatList.find { p0?.mid == it.id }?.status = p0?.status
chatListAdapter.notifyDataSetChanged()
Log.i("TAG", "Mesibo_onMessageStatus: ${p0?.status}}")
}
override fun Mesibo_onActivity(p0: MessageParams?, p1: Int) {
Log.i("TAG", "Mesibo_onActivity: $p1")
when (p1) {
ACTIVITY_ONLINE -> {
imgDotOnline.visible()
imgDotOffline.gone()
Log.i(TAG, "MESIBO_ACTIVITY: activity on")
}
ACTIVITY_TYPING -> Log.i(TAG, "MESIBO_ACTIVITY: user typing")
else -> {
imgDotOnline.gone()
imgDotOffline.visible()
Log.i(TAG, "Mesibo_onActivity: $p1")
}
}
}
override fun Mesibo_onLocation(p0: MessageParams?, p1: Location?) {}
override fun Mesibo_onFile(p0: MessageParams?, p1: FileInfo?) {}
override fun Mesibo_onConnectionStatus(p0: Int) {
when (p0) {
STATUS_ONLINE -> {
setPushToken(ApplicationPrefs.tokenFcm)
tvConnectionStatus.gone()
Log.d(TAG, "MESIBO_CONNECTION_STATUS: online")
}
STATUS_OFFLINE -> {
tvConnectionStatus.gone()
Log.d(TAG, "MESIBO_CONNECTION_STATUS: offline")
}
STATUS_CONNECTING -> {
tvConnectionStatus.visible()
tvConnectionStatus.text = getString(R.string.connecting)
Log.d(TAG, "MESIBO_CONNECTION_STATUS: offline")
}
STATUS_CONNECTFAILURE, STATUS_NONETWORK -> {
tvConnectionStatus.visible()
tvConnectionStatus.text = getString(R.string.mesibo_connect_fail_msg)
}
else -> {
Log.d(TAG, "MESIBO_CONNECTION_STATUS: $p0")
Log.d(TAG, "MESIBO_TOKEN: ${userMesibo?.token}")
}
}
}
override fun Mesibo_onSync(p0: Int) {
if (p0 <= 0) return
readSession.read(p0)
}
private fun onSendMessage() {
val p = MessageParams()
p.profile = userProfile
p.peer = email
p.mid = random()
p.flag = FLAG_DELIVERYRECEIPT or FLAG_READRECEIPT
sendMessage(p, p.mid, message)
chatList.add(
ChatModel(
id = p.mid,
msg = edtSendChat.text.toString(),
date = getTimestamp(),
type = 1,
status = 0
)
)
chatListAdapter.notifyItemInserted(chatList.lastIndex)
layoutManager.scrollToPositionWithOffset(chatList.size - 1, 0)
edtSendChat.text?.clear()
}
private fun loadFromDb(address: String) {
setAppInForeground(this, ChatListActivity.hashCode(), true)
readSession = ReadDbSession(address, this)
readSession.enableReadReceipt(true)
readSession.enableCalls(false)
readSession.enableIncomingCalls(false)
readSession.enableMissedCalls(false)
readSession.enableOutgoingCalls(false)
readSession.enableFifo(true)
readSession.read(500)
}
private fun startMesibo() {
addListener(this)
setSecureConnection(true)
setAccessToken(userMesibo?.token)
setDatabase("db_mesibo", 0)
start()
userProfile = UserProfile()
userProfile?.address = email
setUserProfile(userProfile, true)
}
}
Any clue about how to resolve and debug?
this is my env:
Mesibo AP = 2.7.0
Build tools = 29.0.0
compile SDK versions = 30
appid = com.project.bucynapp
Thanks!
Have you tried the custom scripts or mesibo webhook to debug the issue? Have you referred to the Troubleshooting section in the mesibo push-notification document?
https://mesibo.com/documentation/api/push-notifications/
Unless you post logs and steps you have taken, we have no clue what is happening.
I am a noob in Kotlin.
I've made a streaming music player using Exoplayer2.
For running this music player in background, I tried to bind activity to service.
But it is not working.
I dont know why this player is stopped outside the app even though i add the foreground service.
Can anybody help me? :(
MusicService.kt
var mExoPlayer: SimpleExoPlayer? = null
class MusicService : MediaBrowserServiceCompat() {
private var mMediaSession: MediaSessionCompat? = null
private lateinit var mStateBuilder: PlaybackStateCompat.Builder
private var playbackPosition = 0L
private var currentWindow = 0
private var oldUri: Uri? = null
private val mMediaSessionCallback = object : MediaSessionCompat.Callback() {
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
super.onPlayFromUri(uri, extras)
uri?.let {
val mediaSource = extractMediaSourceFromUri(uri)
if (uri != oldUri)
play(mediaSource)
else play()
oldUri = uri
}
}
override fun onPause() {
super.onPause()
pause()
}
override fun onStop() {
super.onStop()
stop()
}
}
override fun onCreate() {
super.onCreate()
initializePlayer()
initializeExtractor()
initializeAttributes()
mMediaSession = MediaSessionCompat(baseContext, "tag for debugging").apply {
setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS )
mStateBuilder = PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PLAY_PAUSE)
setPlaybackState(mStateBuilder.build())
setCallback(mMediaSessionCallback)
setSessionToken(sessionToken)
isActive = true
}
}
private var mAttrs: AudioAttributes? = null
private fun play(mediaSource: MediaSource) {
if (mExoPlayer == null) initializePlayer()
mExoPlayer?.apply {
mAttrs?.let { initializeAttributes() }
mAttrs?.let { setAudioAttributes(it, true) }
prepare(mediaSource)
seekTo(currentWindow, playbackPosition)
playWhenReady = true
}
}
private fun play() {
mExoPlayer?.apply {
mExoPlayer?.playWhenReady = true
updatePlaybackState(PlaybackStateCompat.STATE_PLAYING)
mMediaSession?.isActive = true
}
}
private fun initializePlayer() {
mExoPlayer = ExoPlayerFactory.newSimpleInstance(
this, DefaultRenderersFactory(baseContext)
, DefaultTrackSelector(),
DefaultLoadControl()
)
}
private fun pause() {
mExoPlayer?.apply {
playWhenReady = false
if (playbackState == PlaybackStateCompat.STATE_PLAYING) {
updatePlaybackState(PlaybackStateCompat.STATE_PAUSED)
}
}
}
private fun stop() {
mExoPlayer?.playWhenReady = false
mExoPlayer?.release()
mExoPlayer = null
updatePlaybackState(PlaybackStateCompat.STATE_NONE)
mMediaSession?.isActive = false
mMediaSession?.release()
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
override fun onDestroy() {
super.onDestroy()
stop()
}
private fun updatePlaybackState(state: Int) {
mMediaSession?.setPlaybackState(
PlaybackStateCompat.Builder().setState(
state
, 0L
, 1.0f // Speed playing
).build()
)
}
private fun initializeAttributes() {
mAttrs = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MUSIC)
.build()
}
private lateinit var mExtractorFactory: ExtractorMediaSource.Factory
private fun initializeExtractor() {
val userAgent = Util.getUserAgent(baseContext, "Application Name")
mExtractorFactory = ExtractorMediaSource.Factory(DefaultDataSourceFactory(this, userAgent))
.setExtractorsFactory(DefaultExtractorsFactory())
}
private fun extractMediaSourceFromUri(uri: Uri): MediaSource {
return mExtractorFactory.createMediaSource(uri)
}
override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
}
override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot? {
return BrowserRoot("", null)
}
playerAct.kt
var audioUrlPass = ""
class playerAct: AppCompatActivity() { private val songUrl: String = audioUrlPass
private lateinit var mMediaBrowserCompat: MediaBrowserCompat
private val connectionCallback: MediaBrowserCompat.ConnectionCallback = object : MediaBrowserCompat.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
mMediaBrowserCompat.sessionToken.also { token ->
val mediaController = MediaControllerCompat(this#playerAct, token)
MediaControllerCompat.setMediaController(this#playerAct, mediaController)
}
playPauseBuild()
}
override fun onConnectionFailed() {
super.onConnectionFailed()
}
}
private val mControllerCallback = object : MediaControllerCompat.Callback() {
}
fun playPauseBuild() {
val mediaController = MediaControllerCompat.getMediaController(this#playerAct)
main_pcv.showTimeoutMs = 0
exo_play.setOnClickListener {
val state = mediaController.playbackState.state
if (state == PlaybackStateCompat.STATE_PAUSED ||
state == PlaybackStateCompat.STATE_STOPPED ||
state == PlaybackStateCompat.STATE_NONE
) {
mediaController.transportControls.playFromUri(Uri.parse(songUrl), null)
}
else if (state == PlaybackStateCompat.STATE_PLAYING ||
state == PlaybackStateCompat.STATE_BUFFERING ||
state == PlaybackStateCompat.STATE_CONNECTING
) {
mediaController.transportControls.pause()
}
}
mediaController.registerCallback(mControllerCallback)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.player_layout)
val componentName = ComponentName(this, MusicService::class.java)
mMediaBrowserCompat = MediaBrowserCompat(
this, componentName, //Identifier for the service
connectionCallback,
null
)
menuButton.setOnClickListener {
super.onBackPressed()
}
btnTime1.setOnClickListener {
val intent = Intent(this, TimerActivity::class.java)
startActivity(intent)
}
playerSeekBar.setOnSeekBarChangeListener(
object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekbar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser){
val volumeNum = progress / 100.0f
mExoPlayer!!.setVolume(volumeNum)
}
}
override fun onStartTrackingTouch(seekbar: SeekBar?) { }
override fun onStopTrackingTouch(seekbar: SeekBar?) { }
})
}
override fun onStart() {
super.onStart()controller
Intent(this, MusicService::class.java).also {
mMediaBrowserCompat.connect()
}
}
override fun onStop() {
super.onStop()
val controllerCompat = MediaControllerCompat.getMediaController(this)
controllerCompat?.unregisterCallback(mControllerCallback)
mMediaBrowserCompat.disconnect()
}
}
In order to start the foreground service, you will need a notification shown within 5 seconds of starting it. Check the following guide in the android documentation: https://developer.android.com/guide/topics/media-apps/audio-app/building-a-mediabrowserservice#mediastyle-notifications
I would like to create and store new users also update the existing users in the following code
What code needs to be added to strore new users in a SQLitedatabase
I have a sqllitehelper class
which has adduser,updateuser,and readuser methods
From what i understand ,the signinfragment activity stores the instance of the logged in user and
if the instance exists then its loaded.When we logout we basically have to make a new user and we login
i would like to create,store,update users using an SQLite database.
package com.google.samples.apps.topeka.fragment
import android.annotation.TargetApi
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.sqlite.SQLiteDatabase
import android.os.Build
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.v4.app.ActivityOptionsCompat
import android.support.v4.app.Fragment
import android.support.v4.util.Pair
import android.support.v4.view.ViewCompat
import android.support.v4.view.animation.FastOutSlowInInterpolator
import android.text.Editable
import android.text.TextWatcher
import android.transition.Transition
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.EditText
import android.widget.GridView
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import com.google.samples.apps.topeka.activity.SignInActivity
import com.google.samples.apps.topeka.adapter.AvatarAdapter
import com.google.samples.apps.topeka.base.R
import com.google.samples.apps.topeka.helper.ActivityLaunchHelper
import com.google.samples.apps.topeka.helper.ApiLevelHelper
import com.google.samples.apps.topeka.helper.DefaultLogin
import com.google.samples.apps.topeka.helper.TAG
import com.google.samples.apps.topeka.helper.TransitionHelper
import com.google.samples.apps.topeka.helper.isLoggedIn
import com.google.samples.apps.topeka.helper.login
import com.google.samples.apps.topeka.helper.onLayoutChange
import com.google.samples.apps.topeka.helper.onSmartLockResult
import com.google.samples.apps.topeka.model.Avatar
import com.google.samples.apps.topeka.model.Player
import com.google.samples.apps.topeka.persistence.PPlayer
import com.google.samples.apps.topeka.widget.TextWatcherAdapter
import com.google.samples.apps.topeka.widget.TransitionListenerAdapter
import com.google.samples.apps.topeka.persistence.TopekaDatabaseHelper;
/**
* Enable selection of an [Avatar] and user name.
*/
class SignInFragment : Fragment() {
private var firstNameView: EditText? = null
private var lastInitialView: EditText? = null
private var doneFab: FloatingActionButton? = null
private var avatarGrid: GridView? = null
private var firstName = ""
private var lastInitial= ""
private var avatar = ""
private val edit by lazy { arguments?.getBoolean(ARG_EDIT, false) ?: false }
private var selectedAvatarView: View? = null
private var player: Player? = null
private var selectedAvatar: Avatar? = null
var a = getContext()
override fun onCreate(savedInstanceState: Bundle?) {
var a = context
val resources = context!!.resources
var ss = TopekaDatabaseHelper(requireActivity())
val newValues = ContentValues().apply {
// Sets the values of each column and inserts the value.
// The arguments to the "put"
// method are "column name" and "value"
}
if (savedInstanceState != null) {
val avatarIndex = savedInstanceState.getInt(KEY_SELECTED_AVATAR_INDEX)
if (avatarIndex != GridView.INVALID_POSITION) {
selectedAvatar = Avatar.values()[avatarIndex]
}
}
activity?.run {
if (isLoggedIn()) {
navigateToCategoryActivity()
Toast.makeText(requireContext(),"old", LENGTH_LONG)
} else {
maketext("new player")
login.loginPlayer(this, ::onSuccessfulLogin)
}
}
super.onCreate(savedInstanceState)
}
/**
* Called when logged in successfully.
*/
private fun onSuccessfulLogin(player: Player) {
if (login != DefaultLogin) return
this.player = player
if (edit) {
with(player) {
firstNameView?.setText(player.firstName)
lastInitialView?.run {
setText(player.lastInitial)
requestFocus()
setSelection(length())
var db = TopekaDatabaseHelper(context)
db.adduser(player.firstName,player.lastInitial)
maketext("saved new")
}
this#SignInFragment.player = player.also {
if (activity != null)
login.savePlayer(activity!!, this, { selectAvatar(it.avatar!!) })
maketext("saved new")
}
}
} else {
Toast.makeText(requireContext(),"new", LENGTH_LONG)
navigateToCategoryActivity()
}
}
private fun maketext(ss:String){
Toast.makeText(requireContext(),ss,LENGTH_LONG)
}
private fun navigateToCategoryActivity() {
activity?.run {
ActivityLaunchHelper.launchCategorySelection(this)
supportFinishAfterTransition()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
activity?.onSmartLockResult(
requestCode,
resultCode,
data,
success = {
player = it
initContents()
navigateToCategoryActivity()
},
failure = {
activity?.run {
login.loginPlayer(this, ::onSuccessfulLogin)
}
}
)
super.onActivityResult(requestCode, resultCode, data)
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val contentView = inflater.inflate(R.layout.fragment_sign_in, container, false)
contentView.onLayoutChange {
avatarGrid?.apply {
adapter = AvatarAdapter(activity!!)
onItemClickListener = AdapterView.OnItemClickListener { _, view, position, _ ->
selectedAvatarView = view
selectedAvatar = Avatar.values()[position]
// showing the floating action button if input data is valid
showFab()
}
numColumns = calculateSpanCount()
selectedAvatar?.run { selectAvatar(this) }
}
}
return contentView
}
/**
* Calculates spans for avatars dynamically.
* #return The recommended amount of columns.
*/
private fun calculateSpanCount(): Int {
val avatarSize = resources.getDimensionPixelSize(R.dimen.size_fab)
val avatarPadding = resources.getDimensionPixelSize(R.dimen.spacing_double)
return (avatarGrid?.width ?: 0) / (avatarSize + avatarPadding)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putInt(KEY_SELECTED_AVATAR_INDEX, (avatarGrid?.checkedItemPosition ?: 0))
super.onSaveInstanceState(outState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
firstNameView = view.findViewById<EditText>(R.id.first_name)
lastInitialView = view.findViewById<EditText>(R.id.last_initial)
doneFab = view.findViewById<FloatingActionButton>(R.id.done)
avatarGrid = view.findViewById<GridView>(R.id.avatars)
if (edit || (player != null && player!!.valid())) {
initContentViews()
initContents()
}
hideEmptyView()
super.onViewCreated(view, savedInstanceState)
}
private fun hideEmptyView() {
view?.run {
findViewById<View>(R.id.empty).visibility = View.GONE
findViewById<View>(R.id.content).visibility = View.VISIBLE
}
}
private fun initContentViews() {
val textWatcher = object : TextWatcher by TextWatcherAdapter {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
// hiding the floating action button if text is empty
if (s.isEmpty()) {
doneFab?.hide()
}
}
// showing the floating action button if avatar is selected and input data is valid
override fun afterTextChanged(s: Editable) {
if (isAvatarSelected() && isInputDataValid()) doneFab?.show()
}
}
firstNameView?.addTextChangedListener(textWatcher)
lastInitialView?.addTextChangedListener(textWatcher)
doneFab?.setOnClickListener {
if (it.id == R.id.done) {
val first = firstNameView?.text?.toString()
val last = lastInitialView?.text?.toString()
activity?.run {
val toSave = player?.apply {
// either update the existing player object
firstName = first
lastInitial = last
avatar = selectedAvatar
} ?: Player(first, last, selectedAvatar) /* or create a new one */
login.savePlayer(this, toSave) {
Toast.makeText(this,"done",LENGTH_LONG)
Log.d(TAG, "Saving login info successful.")
}
}
}
removeDoneFab {
performSignInWithTransition(selectedAvatarView
?: avatarGrid?.getChildAt(selectedAvatar!!.ordinal))
}
}
}
private fun removeDoneFab(endAction: () -> Unit) {
ViewCompat.animate(doneFab)
.scaleX(0f)
.scaleY(0f)
.setInterpolator(FastOutSlowInInterpolator())
.withEndAction(endAction)
.start()
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun performSignInWithTransition(v: View? = null) {
if (v == null || ApiLevelHelper.isLowerThan(Build.VERSION_CODES.LOLLIPOP)) {
// Don't run a transition if the passed view is null
activity?.run {
navigateToCategoryActivity()
}
return
}
if (ApiLevelHelper.isAtLeast(Build.VERSION_CODES.LOLLIPOP)) {
activity?.run {
window.sharedElementExitTransition.addListener(object :
Transition.TransitionListener by TransitionListenerAdapter {
override fun onTransitionEnd(transition: Transition) {
finish()
}
})
val pairs = TransitionHelper.createSafeTransitionParticipants(this, true,
Pair(v, getString(R.string.transition_avatar)))
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, *pairs)
ActivityLaunchHelper.launchCategorySelection(this, options)
}
}
}
private fun initContents() {
player?.run {
valid().let {
firstNameView?.setText(firstName)
lastInitialView?.setText(lastInitial)
avatar?.run { selectAvatar(this) }
}
}
}
private fun isAvatarSelected() = selectedAvatarView != null || selectedAvatar != null
private fun selectAvatar(avatar: Avatar) {
selectedAvatar = avatar
avatarGrid?.run {
requestFocusFromTouch()
setItemChecked(avatar.ordinal, true)
}
showFab()
}
private fun showFab() {
if (isInputDataValid()) doneFab?.show()
}
private fun isInputDataValid() =
firstNameView?.text?.isNotEmpty() == true &&
lastInitialView?.text?.isNotEmpty() == true &&
selectedAvatar != null
companion object {
private const val ARG_EDIT = "EDIT"
private const val KEY_SELECTED_AVATAR_INDEX = "selectedAvatarIndex"
fun newInstance(edit: Boolean = false): SignInFragment {
return SignInFragment().apply {
arguments = Bundle().apply {
putBoolean(ARG_EDIT, edit)
}
}
}
}
}
I have recently tried to use ExoPlayer from google in my android project. Exoplayer is keep playing video even I have call all the necessary stuff & player is not being released.It appear releasePlayer() doesn't works at all. Video keep playing even if i move to other activity. I have tried all the solutions available on stack or codelabs sites etc. Which makes me to put this question here on stack.
package com.android.maccino.ui
import android.content.Intent
import android.media.MediaPlayer
import android.net.Uri
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.android.maccino.R
import com.android.maccino.models.SponsorModel
import com.android.maccino.utils.AppConstants
import com.android.maccino.utils.AppUtils
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.*
import com.kaopiz.kprogresshud.KProgressHUD
import com.orhanobut.logger.Logger
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_dashboard.*
import java.util.*
class DashboardActivity : AppCompatActivity() {
lateinit var sponsorModel: SponsorModel
lateinit var mDatabaseRef: DatabaseReference
lateinit var kProgressHud: KProgressHUD
lateinit var mSponsorList: MutableList<SponsorModel>
lateinit var player: SimpleExoPlayer
var currentWindow: Int = 0
var playbackPosition: Long = 0;
var wasPlayingBefore: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard)
mDatabaseRef = FirebaseDatabase.getInstance().reference
sponsorModel = SponsorModel()
mSponsorList = ArrayList<SponsorModel>()
Logger.d(FirebaseAuth.getInstance().currentUser!!.uid)
if (intent != null &&
intent.getSerializableExtra(AppConstants.INTENT_SPONSOR_MODEL) != null) {
sponsorModel = intent.getSerializableExtra(AppConstants.INTENT_SPONSOR_MODEL) as SponsorModel
setValues()
}
btnAnswer1.setOnClickListener {
if (sponsorModel.answers.get(0).getValue(btnAnswer1.text.toString()) == true) {
makeAnsweredSponsorEntry()
startActivity(Intent(this#DashboardActivity, CorrectAnswer::class.java).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra(AppConstants.INTENT_SPONSOR_MODEL, sponsorModel))
this.finish()
} else if (sponsorModel.answers.get(0).getValue(btnAnswer1.text.toString()) == false) {
startActivity(Intent(this#DashboardActivity, WrongAnswer::class.java))
}
}
btnAnswer2.setOnClickListener {
if (sponsorModel.answers.get(1).getValue(btnAnswer2.text.toString()) == true) {
makeAnsweredSponsorEntry()
startActivity(Intent(this#DashboardActivity, CorrectAnswer::class.java).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra(AppConstants.INTENT_SPONSOR_MODEL, sponsorModel))
this.finish()
} else if (sponsorModel.answers.get(1).getValue(btnAnswer2.text.toString()) == false) {
startActivity(Intent(this#DashboardActivity, WrongAnswer::class.java))
}
}
btnAnswer3.setOnClickListener {
if (sponsorModel.answers.get(2).getValue(btnAnswer3.text.toString()) == true) {
makeAnsweredSponsorEntry()
startActivity(Intent(this#DashboardActivity, CorrectAnswer::class.java).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra(AppConstants.INTENT_SPONSOR_MODEL, sponsorModel))
this.finish()
} else if (sponsorModel.answers.get(2).getValue(btnAnswer3.text.toString()) == false) {
startActivity(Intent(this#DashboardActivity, WrongAnswer::class.java))
}
}
tvSearch.setOnClickListener { startActivity(Intent(this#DashboardActivity, SearchActivity::class.java).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)) }
}
override fun onResume() {
super.onResume()
initializePlayer()
}
override fun onPause() {
super.onPause()
if (Util.SDK_INT <= 23) {
releasePlayer();
}
}
private fun setValues() {
try {
tvQuestion.setText(sponsorModel.question)
sponsorModel.answers.get(0).keys.forEach { key: String ->
btnAnswer1.setText(key)
}
sponsorModel.answers.get(1).keys.forEach { key: String ->
btnAnswer2.setText(key)
}
sponsorModel.answers.get(2).keys.forEach { key: String ->
btnAnswer3.setText(key)
}
tvSponsorName.setText(sponsorModel.sponsor_name)
Picasso.with(this#DashboardActivity).load(sponsorModel.logo).into(ivSponsorLogo)
ivFlag.setOnClickListener { startActivity(Intent(Intent.ACTION_VIEW).setDataAndType(Uri.parse(sponsorModel.video_url), "video/*")) }
initializePlayer()
} catch (e: Exception) {
e.printStackTrace()
}
}
//Player implementation
private fun initializePlayer() {
try {
player = ExoPlayerFactory.newSimpleInstance(
DefaultRenderersFactory(this#DashboardActivity),
DefaultTrackSelector(), DefaultLoadControl())
player.repeatMode = Player.REPEAT_MODE_ALL
playerView.player = player
playerView.hideController()
player.setPlayWhenReady(true)
player.seekTo(currentWindow, playbackPosition);
val uri = Uri.parse(sponsorModel.video_url)
val mediaSource = buildMediaSource(uri)
player.prepare(mediaSource, true, false)
wasPlayingBefore = true
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun buildMediaSource(uri: Uri): MediaSource {
return ExtractorMediaSource.Factory(
DefaultHttpDataSourceFactory("exoplayer-maccino")).createMediaSource(uri)
}
private fun releasePlayer() {
if (player != null) {
playbackPosition = player.currentPosition
currentWindow = player.currentWindowIndex
player.playWhenReady = false
player.setRepeatMode(Player.REPEAT_MODE_OFF)
player.stop()
player.release()
playerView.player = null
// player = null
}
}
}