Android Notification is not canceling after workManager completes - android

I am using Android WorkManager with setForegroundAsync, But the notification is not canceling after workManager completes.
OfflinePushWorkManager is a work manager class to push offline stuff to the server.
class OfflinePushWorkManager(var context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams),
KoinComponent {
private val reflectionUtil: ReflectionUtil by inject()
private val apiService: ApiService by inject()
private val flowchartDao: FlowChartDao by inject()
val flowchartUploadObserver = MutableLiveData<ApiResponse<BaseResponse<SaveFlowchartResponse>>>()
val deleteFlowchart = MutableLiveData<ApiResponse<Unit>>()
override suspend fun doWork(): Result {
setForegroundAsync(createForegroundInfo())
val data = inputData.getString(Constants.BundleKeys.FLOWCHART)
// val docString : String? = DocumentReaderUtil.readTxtFromUri(Uri.parse(data.toString()), applicationContext)
Log.d("uri is ", data.toString())
val dataString: String? = AppUtils.readStringFromFileInternalStorage(applicationContext, data)
Log.d("dataString is ", dataString+"")
dataString?.let {
val flowchart = reflectionUtil.gsonToClass(it, FlowChart::class.java)
uploadFlowchart(flowchart)
AppLogs.e("classObject", "" + flowchart)
}
return Result.success()
}
companion object{
var notificationManager:NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
var uploadedProgress : Int = 1
var notification: NotificationCompat.Builder? = null
fun updateProgress(progress : Int?){
if(progress == 100){
uploadedProgress = 100
cancelNotification()
AppLogs.e("uploadedProgress", " " + uploadedProgress)
}
else {
uploadedProgress = uploadedProgress + 30
if(uploadedProgress < 100 && uploadedProgress < 70)
notification?.setProgress(100,uploadedProgress,false)
AppLogs.e("uploadedProgress", " " + uploadedProgress)
if(notification != null) {
Log.d("Notification CancelAll---->", "5 Notifying")
notificationManager.notify(1, notification?.build())
}
}
}
fun cancelNotification(){
Log.d("Notification CancelAll---->", "3")
notificationManager.cancelAll()
}
fun displayNotification(title: String) : Notification {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
Constants.SCULPTOR,
Constants.SCULPTOR,
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager?.createNotificationChannel(channel)
}
notification = NotificationCompat.Builder(
context,
Constants.SCULPTOR
)
.setContentTitle(title)
.setSmallIcon(R.mipmap.ic_launcher)
.setOnlyAlertOnce(true)
notification?.setProgress(100,uploadedProgress,false)
Log.d("Notification CancelAll---->", "4 Notifying")
notificationManager?.notify(1, notification?.build())
return notification!!.build()
}
}
/**
* Create ForegroundInfo required to run a Worker in a foreground service.
*/
private fun createForegroundInfo(): ForegroundInfo {
val notificationId = 1
return ForegroundInfo(notificationId, displayNotification("Uploading in progress...."))
}
}
Calling Sequense
private fun setWorkManager(list: Array<FlowChart>) {
Log.d("---->", "${list.size} {list.size}")
val gsonList:ArrayList<String> = ArrayList()
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
workManager = WorkManager.getInstance(applicationContext)
OfflinePushWorkManager.uploadedProgress = 0
for( i in 0 until list.size) {
val listData = reflectionUtil.gsonFromClass(list.get(i))
gsonList.add(listData)
if(mPref.get(PreferenceConstants.TOKEN,"").isNullOrEmpty()){
workManager?.cancelAllWork()
OfflinePushWorkManager.cancelNotification()
break
}
val dataString: String = AppUtils.writeStringToFileInternalStorage(applicationContext, listData, i.toString())
val data: Data = Data.Builder().putString(Constants.BundleKeys.FLOWCHART,dataString).build()
val workManagerRequest = OneTimeWorkRequest.Builder(OfflinePushWorkManager::class.java).setInputData(data)
.setConstraints(constraints).build()
workManager?.enqueue(workManagerRequest)
mPref.put(PreferenceConstants.WORK_ID,workManagerRequest.id.toString())
handleWorkManagerState(workManagerRequest,i,list)
}
}
private fun cancelNotification(){
workManager?.pruneWork()
GlobalScope.launch {
val nMgr = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
nMgr.activeNotifications.forEach {
nMgr.cancel(it.id)
Log.d("Notification Cancel---->", "1 ${it.id}")
}
Log.d("Notification CancelAll---->", "2")
nMgr.cancelAll()
}
}
private fun handleWorkManagerState(workManagerRequest: OneTimeWorkRequest, i: Int, list: Array<FlowChart>) {
workManager?.getWorkInfoByIdLiveData(workManagerRequest.id)
?.observe(this, object : androidx.lifecycle.Observer<WorkInfo> {
override fun onChanged(t: WorkInfo?) {
if(t?.state == WorkInfo.State.RUNNING ){
mPref.put(PreferenceConstants.IS_WORK_RUNNING,true)
OfflinePushWorkManager.updateProgress(0)
} else if(t?.state == WorkInfo.State.SUCCEEDED){
if(i+1 == list.size){
mPref.put(PreferenceConstants.IS_WORK_RUNNING,false)
OfflinePushWorkManager.updateProgress(100)
workManager?.cancelAllWork()
OfflinePushWorkManager.cancelNotification()
cancelNotification()
} else{
OfflinePushWorkManager.updateProgress(0)
}
} else if(t?.state == WorkInfo.State.FAILED){
if(i+1 == list.size) {
mPref.put(PreferenceConstants.IS_WORK_RUNNING,false)
workManager?.cancelAllWork()
OfflinePushWorkManager.cancelNotification()
cancelNotification()
}
} else if(t?.state == WorkInfo.State.CANCELLED){
mPref.put(PreferenceConstants.IS_WORK_RUNNING,false)
workManager?.cancelAllWork()
OfflinePushWorkManager.cancelNotification()
cancelNotification()
mPref.put(PreferenceConstants.WORK_ID,"")
}
}
})
}

Related

How do i make Android recognize what song is playing?

I have a music player but i noticed that android and other devices don't recognize what song is playing.
For example in mi band 4 i can control the music if i have it on youtube music but not in my app or even in my launcher the at a glance widget recognizes it's playing.
Example:
Now that you know the context, what do i need to make it work or what am i missing?
Here's my service that plays the music:
class SimpleMPService: Service() {
private val mBinder = LocalBinder()
private lateinit var notification: Notification
private lateinit var notificationManager: NotificationManager
var playList = ArrayList<Song>()
private var shuffledPlaylist = ArrayList<Song>()
var currentSongPosition: Int = 0
private var currentSongPath: String = ""
var onRepeatMode = false
private lateinit var audioManager: AudioManager
//Listeners
var onMusicSelectedListener: OnMusicSelectedListener? = null
var onMusicSelectedListenerToQueue: OnMusicSelectedListenerToQueue? = null //Since there is no way to have two listeners at same time it needs another listener to the queue list
var onMusicPausedListener: OnMusicPausedListener? = null
var onPlaylistAdded: OnPlaylistsAdded? = null
var onMusicResumedListener: OnMusicResumedListener? = null
var onMusicSecondPassedListener: OnSecondPassedListener? = null
var onMusicShuffleToggledListener: OnMusicShuffleToggledListener? = null
var onMediaPlayerStoppedListener: OnMediaPlayerStoppedListener? = null
//Player States
private var serviceStarted = false
var musicShuffled = false
private var musicStarted = false
//Others
private lateinit var mediaButtonReceiver: ComponentName
private lateinit var mediaSession: MediaSessionCompat
inner class LocalBinder : Binder() {
fun getService(): SimpleMPService = this#SimpleMPService
}
companion object {
private val mediaPlayer = MediaPlayer()
fun startService(context: Context) {
val startIntent = Intent(context, SimpleMPService::class.java)
context.startForegroundService(startIntent)
}
}
override fun onBind(intent: Intent?): IBinder {
val context = this
mediaButtonReceiver = ComponentName(context, ReceiverPlayPause::class.java)
mediaSession = MediaSessionCompat(context, "SessionTag")
mediaSession.setCallback(object : MediaSessionCompat.Callback(){
override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean {
val ke = mediaButtonIntent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
if( ke?.action == KeyEvent.ACTION_DOWN ){
if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS )
previousSong( context )
if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE )
pauseResumeMusic( context )
if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY )
pauseResumeMusic( context )
if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_NEXT )
skipSong( context )
}
return super.onMediaButtonEvent(mediaButtonIntent)
}
})
return mBinder
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
override fun onCreate() {
super.onCreate()
notificationManager = this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
fun getCurrentPlaylist(): ArrayList<Song>{
return if(!musicShuffled) playList else shuffledPlaylist
}
fun isMusicPlayingOrPaused(): Boolean{ return musicStarted }
fun toggleShuffle(){
if( !musicShuffled ){
musicShuffled = true
shuffledPlaylist = ArrayList()
val tempShuffledPlaylist = ArrayList<Song>()
//Adds the current song to first position
playList.forEach { song ->
if (song.path != currentSongPath)
tempShuffledPlaylist.add(song)
else
shuffledPlaylist.add( song )
}
//Shuffles the temp playlist and adds it to the one with just the current song
tempShuffledPlaylist.shuffle()
for( song in tempShuffledPlaylist )
shuffledPlaylist.add( song )
currentSongPosition = 0
}
else{
musicShuffled = false
for( i in playList.indices ){
if( playList[i].path == currentSongPath ){
currentSongPosition = i
break
}
}
}
onMusicShuffleToggledListener?.onMusicShuffleToggled(musicShuffled)
}
fun enableShuffle(){
musicShuffled = true
shuffledPlaylist = ArrayList(playList)
shuffledPlaylist.shuffle()
onMusicShuffleToggledListener?.onMusicShuffleToggled(true)
currentSongPosition = 0
}
fun setPlaylist( newPlaylist: ArrayList<Song> ){ playList = newPlaylist }
fun playSongAndEnableShuffle(context: Context, position: Int){
val selectedSong = playList[position]
shuffledPlaylist = ArrayList(playList)
shuffledPlaylist.shuffle()
shuffledPlaylist.removeIf{ it.path == selectedSong.path }
shuffledPlaylist.add(0, selectedSong )
currentSongPosition = 0
playSong(context)
musicShuffled = true
}
fun isMusicPlaying(): Boolean{
return mediaPlayer.isPlaying
}
fun getCurrentSongPath(): String{ return currentSongPath }
private val audioFocusChangeListener = OnAudioFocusChangeListener { focusChange ->
when (focusChange) {
AudioManager.AUDIOFOCUS_GAIN -> {}
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT->{}
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
if( mediaPlayer.isPlaying )
pauseMusic(this )
}
AudioManager.AUDIOFOCUS_LOSS -> {
if( mediaPlayer.isPlaying )
pauseMusic(this )
}
}
}
private val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
setAudioAttributes(AudioAttributes.Builder().run {
setUsage(AudioAttributes.USAGE_MEDIA)
setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
build()
})
setAcceptsDelayedFocusGain(true)
setOnAudioFocusChangeListener(audioFocusChangeListener)
build()
}
fun playSong(context: Context){
serviceStarted = true
musicStarted = true
val songPath: String
val songTitle: String
val songArtist: String
val songID: Long
val songAlbumID: Long
val songAlbumArt: Bitmap
val songDuration: Int
if( !musicShuffled ) {
songPath = playList[currentSongPosition].path
songTitle = playList[currentSongPosition].title
songArtist = playList[currentSongPosition].artistName
songID = playList[currentSongPosition].id
songAlbumID = playList[currentSongPosition].albumID
songAlbumArt = GetSongs.getSongAlbumArt(context, songID, songAlbumID)
songDuration = playList[currentSongPosition].duration
}
else{
songPath = shuffledPlaylist[currentSongPosition].path
songTitle = shuffledPlaylist[currentSongPosition].title
songArtist = shuffledPlaylist[currentSongPosition].artistName
songID = shuffledPlaylist[currentSongPosition].id
songAlbumID = shuffledPlaylist[currentSongPosition].albumID
songAlbumArt = GetSongs.getSongAlbumArt(context, songID, songAlbumID)
songDuration = shuffledPlaylist[currentSongPosition].duration
}
currentSongPath = songPath
val isAudioLimited = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("setting_limitAudioVolume", true)
mediaPlayer.reset()
mediaPlayer.setDataSource(songPath)
when(isAudioLimited){
true-> mediaPlayer.setVolume(0.08F, 0.08F)
false-> mediaPlayer.setVolume(0.1F, 0.1F)
}
mediaPlayer.prepareAsync()
mediaPlayer.setOnPreparedListener {
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
requestPlayWithFocus()
mediaSession.isActive = true
//Open App
val openAppIntent = Intent( context, ActivityMain::class.java )
val pendingOpenAppIntent = TaskStackBuilder.create( context ).run{
addNextIntentWithParentStack(openAppIntent)
getPendingIntent( 0, PendingIntent.FLAG_IMMUTABLE )
}
//Stop Service
val stopIntent = Intent(context, ReceiverStop::class.java )
val pendingStopIntent = PendingIntent.getBroadcast( context, 1, stopIntent, PendingIntent.FLAG_IMMUTABLE )
//Previous Music
val previousSongIntent = Intent(context, ReceiverPreviousSong::class.java )
val pendingPreviousSongIntent = PendingIntent.getBroadcast( context, 1, previousSongIntent, PendingIntent.FLAG_IMMUTABLE )
//Pauses/Plays music
val playPauseIntent = Intent(context, ReceiverPlayPause::class.java )
val pendingPlayPauseIntent = PendingIntent.getBroadcast( context, 1, playPauseIntent, PendingIntent.FLAG_IMMUTABLE )
//Skips to next music
val skipSongIntent = Intent(context, ReceiverSkipSong::class.java )
val pendingSkipSongIntent = PendingIntent.getBroadcast( context, 1, skipSongIntent, PendingIntent.FLAG_IMMUTABLE )
notification = NotificationCompat.Builder(context, "Playback")
.setContentIntent( pendingOpenAppIntent )
.setStyle( androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.sessionToken)
.setShowActionsInCompactView(1, 2, 3)
)
.setSmallIcon(R.drawable.icon)
.addAction(R.drawable.icon_x, "Stop Player", pendingStopIntent )
.addAction(R.drawable.icon_previous_notification, "Previous Music", pendingPreviousSongIntent )
.addAction(R.drawable.icon_pause_notification, "Play Pause Music", pendingPlayPauseIntent )
.addAction(R.drawable.icon_next_notification, "Next Music", pendingSkipSongIntent )
.build()
mediaSession.setMetadata(
MediaMetadataCompat.Builder()
.putString(MediaMetadata.METADATA_KEY_TITLE, songTitle)
.putString(MediaMetadata.METADATA_KEY_ARTIST, songArtist)
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, songAlbumArt)
.putLong(MediaMetadata.METADATA_KEY_DURATION, songDuration.toLong())
.build()
)
startForeground( 2, notification )
notificationManager.notify( 2, notification )
}
handleSongFinished( context )
if( !musicShuffled ) {
onMusicSelectedListener?.onMusicSelected(playList, currentSongPosition)
onMusicSelectedListenerToQueue?.onMusicSelected(playList, currentSongPosition)
}
else {
onMusicSelectedListener?.onMusicSelected(shuffledPlaylist, currentSongPosition)
onMusicSelectedListenerToQueue?.onMusicSelected(shuffledPlaylist, currentSongPosition)
}
val bluetoothReceiver = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
context.registerReceiver(bluetoothBroadcastReceiver, bluetoothReceiver )
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post( object : Runnable{
override fun run() {
if( onMusicSecondPassedListener != null )
onMusicSecondPassedListener?.onSecondPassed( mediaPlayer.currentPosition )
mainHandler.postDelayed( this,1000)
}
})
}
private val bluetoothBroadcastReceiver = object : BroadcastReceiver(){
override fun onReceive(p0: Context?, p1: Intent?) {
if(isMusicPlaying()) pauseMusic(p0!!)
}
}
fun seekTo( position: Int){
val newSongPosition = position * 1000
mediaPlayer.seekTo(newSongPosition)
if( !mediaPlayer.isPlaying ) mediaPlayer.start()
}
private fun handleSongFinished(context: Context) {
mediaPlayer.setOnCompletionListener{
//If loop mode is activated
if( onRepeatMode ){
playSong( context )
}
//Is it's the last song
else if( (currentSongPosition + 1) == playList.size ){
stopMediaPlayer()
}
else{
currentSongPosition++
playSong( context )
}
}
}
fun toggleLoop(){
onRepeatMode = !onRepeatMode
}
fun stopMediaPlayer(){
onMediaPlayerStoppedListener?.onMediaPlayerStopped()
mediaPlayer.stop()
currentSongPosition = -1
currentSongPath = ""
stopForeground(true)
stopSelf()
}
fun skipSong(context: Context){
if( (currentSongPosition + 1) < playList.size ){
currentSongPosition ++
playSong( context )
}
}
fun previousSong(context: Context){
if( (currentSongPosition - 1) >= 0 ){
currentSongPosition--
playSong( context )
}
}
#Suppress("DEPRECATION")
fun pauseMusic(context: Context ){
val playPauseIcon = R.drawable.icon_play_notification
mediaPlayer.pause()
mediaSession.isActive = false
if( onMusicPausedListener != null)
onMusicPausedListener?.onMusicPaused()
//Updates the notification
val playPauseIntent = Intent(context, ReceiverPlayPause::class.java )
playPauseIntent.putExtra( "action", "playPause" )
val pendingPlayPauseIntent = PendingIntent.getBroadcast( context, 1, playPauseIntent, PendingIntent.FLAG_IMMUTABLE )
notification.actions[2] = Notification.Action( playPauseIcon, "Play Music", pendingPlayPauseIntent )
startForeground( 2, notification )
notificationManager.notify( 2, notification )
}
#Suppress("DEPRECATION")
fun pauseResumeMusic(context: Context ){
val playPauseIcon: Int
if( mediaPlayer.isPlaying ) {
playPauseIcon = R.drawable.icon_play_notification
mediaPlayer.pause()
if( onMusicPausedListener != null) onMusicPausedListener?.onMusicPaused()
}
else {
playPauseIcon = R.drawable.icon_pause_notification
if( onMusicResumedListener != null ) onMusicResumedListener?.onMusicResumed()
requestPlayWithFocus()
}
//Updates the notification
val playPauseIntent = Intent(context, ReceiverPlayPause::class.java )
playPauseIntent.putExtra( "action", "playPause" )
val pendingPlayPauseIntent = PendingIntent.getBroadcast( context, 1, playPauseIntent, PendingIntent.FLAG_IMMUTABLE )
notification.actions[2] = Notification.Action( playPauseIcon, "Play Music", pendingPlayPauseIntent )
startForeground( 2, notification )
notificationManager.notify( 2, notification )
}
private fun requestPlayWithFocus(){
val focusLock = Any()
val res = audioManager.requestAudioFocus(focusRequest)
synchronized(focusLock) {
when (res) {
AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
mediaPlayer.start()
onMusicResumedListener?.onMusicResumed()
true
}
else -> false
}
}
}
fun updatePlaylists(){ onPlaylistAdded?.onPlaylistAdded() }
//////////////////////////////////////////////////////////////////////////////////////////////////////
interface OnMusicSelectedListener{ fun onMusicSelected(playList: ArrayList<Song>, position: Int ) }
interface OnMusicSelectedListenerToQueue{ fun onMusicSelected(playList: ArrayList<Song>, position: Int ) }
interface OnPlaylistsAdded{ fun onPlaylistAdded() }
interface OnMusicPausedListener{ fun onMusicPaused() }
interface OnMusicResumedListener{ fun onMusicResumed() }
interface OnSecondPassedListener{ fun onSecondPassed(position: Int ) }
interface OnMusicShuffleToggledListener{ fun onMusicShuffleToggled(state: Boolean) }
interface OnMediaPlayerStoppedListener{ fun onMediaPlayerStopped() }
}
I wanted to make other devices recognize what song is playing and map the respective buttons like previous pause play next. I tried to search but didn't find anything useful. I don't know what to search specifically.
Thanks in advance :D
I finally found the problem. I wass missing Playback State And Callbacks.
I am sharing what i did in case anyone ends up here and in the same situation.
First i added other callbacks to media session callback. Mainly this ones:
override fun onPlay() {
super.onPlay()
pauseResumeMusic(context)
}
override fun onStop() {
super.onStop()
stopMediaPlayer()
}
override fun onPause() {
super.onPause()
pauseResumeMusic(context)
}
override fun onSkipToNext() {
super.onSkipToNext()
selectNextSong(context)
}
override fun onSkipToPrevious() {
super.onSkipToPrevious()
selectPreviousSong(context)
}
After that i needed to set the state when those actions happened. For that i made a function to set it.
fun setPlaybackState(state: Int){
val stateBuilder = PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE
or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
or PlaybackStateCompat.ACTION_SKIP_TO_NEXT)
.apply {
setState(state, mediaPlayer.currentPosition.toLong(), 1.0f)
}
mediaSession.setPlaybackState(stateBuilder.build())
}
Now everytime i need to update i just call the function and send the state as a parameter.
Example:
setPlaybackState(PlaybackStateCompat.STATE_PLAYING)
Hope this helps someone in case it was stuck like me for months XD

how do i get the set notification in BLE nordic

i am trying to set notification callback in BLE nordic, where i am using the Android BLE library (Nordic Github). But i not ablet to get the notification event when i am changing the value of the characteristics.
`
class BleManagerHP1T(context: Context) : BleManager(context) {
override fun getGattCallback(): BleManagerGattCallback = GattCallback()
override fun log(priority: Int, message: String) {
if (BuildConfig.DEBUG || priority == Log.ERROR) {
Log.println(priority, GattService.TAG, message)
}
}
private inner class GattCallback : BleManagerGattCallback() {
private var myCharacteristic: BluetoothGattCharacteristic? = null
private var rxCharacteristic: BluetoothGattCharacteristic? = null
#SuppressLint("MissingPermission")
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
val service = gatt.getService(GattService.MyServiceProfile.MY_SERVICE_UUID)
myCharacteristic =
service?.getCharacteristic(GattService.MyServiceProfile.MY_CHARACTERISTIC_UUID)
val myCharacteristicProperties = myCharacteristic?.properties ?: 0
Log.d(TAG, "isRequiredServiceSupported: notify ${(myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0)}")
rxCharacteristic = service?.getCharacteristic(GattService.MyServiceProfile.RX_CHARACTERISTIC_UUID)
val obj = JSONObject()
obj.put("OPCODE","PROVISION")
rxCharacteristic?.value = obj.toString().encodeToByteArray()
val rxRead = gatt.writeCharacteristic(rxCharacteristic)
Log.d(TAG, "isRequiredServiceSupported: Read $rxRead")
return (myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0)
}
override fun initialize() {
enableNotifications(myCharacteristic).enqueue()
requestMtu(260).enqueue();
setNotificationCallback(myCharacteristic).with { _, data ->
Log.d(TAG, "initialize: TX char Notification Called")
if (data.value != null) {
val value = String(data.value!!, Charsets.UTF_8)
Log.d(TAG, "initialize: TX char value $value")
}
}
requestMtu(260).enqueue();
enableNotifications(rxCharacteristic).enqueue()
setNotificationCallback(rxCharacteristic).with { _, data ->
Log.d(TAG, "initialize: RX char Notification Called")
if (data.value != null) {
val value = String(data.value!!, Charsets.UTF_8)
Log.d(TAG, "initialize: RX char value $value")
}
}
beginAtomicRequestQueue()
.add(enableNotifications(myCharacteristic)
.fail { _: BluetoothDevice?, status: Int ->
log(Log.ERROR, "Could not subscribe: $status")
disconnect().enqueue()
}
)
.done {
log(Log.INFO, "Target initialized")
}
.enqueue()
}
override fun onServicesInvalidated() {
myCharacteristic = null
}
}
override fun readCharacteristic(characteristic: BluetoothGattCharacteristic?): ReadRequest {
return Request.newReadRequest(characteristic)
}
}
`
gatt connection is establishing perfectly fine, using this code.
`
val bleManager = BleManagerHP1T(this#ControllerActivity)
synchronized (this) {
bleManager.connect(deviceMainList[position]).useAutoConnect(false).enqueue()
}
`
here is the gatt service file .
`
class GattService : Service() {
private val defaultScope = CoroutineScope(Dispatchers.Default)
private lateinit var bluetoothObserver: BroadcastReceiver
private var myCharacteristicChangedChannel: SendChannel<String>? = null
private val clientManagers = mutableMapOf<String, ClientManager>()
// val connect = BleManager()
#RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
// Setup as a foreground service
val notificationChannel = NotificationChannel(
GattService::class.java.simpleName,
resources.getString(R.string.gatt_service_name),
NotificationManager.IMPORTANCE_DEFAULT
)
val notificationService =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationService.createNotificationChannel(notificationChannel)
val notification = NotificationCompat.Builder(this, GattService::class.java.simpleName)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(resources.getString(R.string.gatt_service_name))
.setContentText(resources.getString(R.string.gatt_service_running_notification))
.setAutoCancel(true)
startForeground(1, notification.build())
// Observe OS state changes in BLE
bluetoothObserver = object : BroadcastReceiver() {
#SuppressLint("MissingPermission")
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
BluetoothAdapter.ACTION_STATE_CHANGED -> {
val bluetoothState = intent.getIntExtra(
BluetoothAdapter.EXTRA_STATE,
-1
)
when (bluetoothState) {
BluetoothAdapter.STATE_ON -> enableBleServices()
BluetoothAdapter.STATE_OFF -> disableBleServices()
}
}
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
val device =
intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
Log.d(TAG, "Bond state changed for device ${device?.address}: ${device?.bondState}")
when (device?.bondState) {
BluetoothDevice.BOND_BONDED -> addDevice(device)
BluetoothDevice.BOND_NONE -> removeDevice(device)
}
}
}
}
}
registerReceiver(bluetoothObserver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
registerReceiver(bluetoothObserver, IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED))
// Startup BLE if we have it
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
if (bluetoothManager.adapter?.isEnabled == true) enableBleServices()
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(bluetoothObserver)
disableBleServices()
}
override fun onBind(intent: Intent?): IBinder? =
when (intent?.action) {
DATA_PLANE_ACTION -> {
DataPlane()
}
else -> null
}
override fun onUnbind(intent: Intent?): Boolean =
when (intent?.action) {
DATA_PLANE_ACTION -> {
myCharacteristicChangedChannel = null
true
}
else -> false
}
/**
* A binding to be used to interact with data of the service
*/
inner class DataPlane : Binder() {
fun setMyCharacteristicChangedChannel(sendChannel: SendChannel<String>) {
myCharacteristicChangedChannel = sendChannel
}
}
#SuppressLint("MissingPermission")
private fun enableBleServices() {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
if (bluetoothManager.adapter?.isEnabled == true) {
Log.i(TAG, "Enabling BLE services")
bluetoothManager.adapter.bondedDevices.forEach { device -> addDevice(device) }
} else {
Log.w(TAG, "Cannot enable BLE services as either there is no Bluetooth adapter or it is disabled")
}
}
private fun disableBleServices() {
clientManagers.values.forEach { clientManager ->
clientManager.close()
}
clientManagers.clear()
}
private fun addDevice(device: BluetoothDevice) {
if (!clientManagers.containsKey(device.address)) {
val clientManager = ClientManager()
clientManager.connect(device).useAutoConnect(true).enqueue()
clientManagers[device.address] = clientManager
}
}
private fun removeDevice(device: BluetoothDevice) {
clientManagers.remove(device.address)?.close()
}
/*
* Manages the entire GATT service, declaring the services and characteristics on offer
*/
companion object {
/**
* A binding action to return a binding that can be used in relation to the service's data
*/
const val DATA_PLANE_ACTION = "data-plane"
const val TAG = "gatt-service"
}
private inner class ClientManager : BleManager(this#GattService) {
override fun getGattCallback(): BleManagerGattCallback = GattCallback()
override fun log(priority: Int, message: String) {
if (BuildConfig.DEBUG || priority == Log.ERROR) {
Log.println(priority, TAG, message)
Log.d(TAG, "log: $message")
}
}
private inner class GattCallback : BleManagerGattCallback() {
private var myCharacteristic: BluetoothGattCharacteristic? = null
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
val service = gatt.getService(MyServiceProfile.MY_SERVICE_UUID)
myCharacteristic =
service?.getCharacteristic(MyServiceProfile.MY_CHARACTERISTIC_UUID)
val myCharacteristicProperties = myCharacteristic?.properties ?: 0
return (myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_READ != 0) &&
(myCharacteristicProperties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0)
}
override fun initialize() {
setNotificationCallback(myCharacteristic).with { _, data ->
if (data.value != null) {
val value = String(data.value!!, Charsets.UTF_8)
defaultScope.launch {
myCharacteristicChangedChannel?.send(value)
}
}
}
beginAtomicRequestQueue()
.add(enableNotifications(myCharacteristic)
.fail { _: BluetoothDevice?, status: Int ->
log(Log.ERROR, "Could not subscribe: $status")
disconnect().enqueue()
}
)
.done {
log(Log.INFO, "Target initialized")
}
.enqueue()
}
override fun onServicesInvalidated() {
myCharacteristic = null
}
}
}
object MyServiceProfile {
val MY_SERVICE_UUID: UUID = UUID.fromString("8d67d51a-801b-43cb-aea2-bbec9d1211fd")
val MY_CHARACTERISTIC_UUID: UUID = UUID.fromString("8d67d51c-801b-43cb-aea2-bbec9d1211fd")
val RX_CHARACTERISTIC_UUID: UUID = UUID.fromString("8d67d51b-801b-43cb-aea2-bbec9d1211fd")
}
}
`

Launch new Activity from FrameLayout

I try to study overlay library for kotlin applications https://github.com/KoderLabs/overlay-service. Right now I have a problem related with opening a new activity from a button which is located inside a FrameLayout class.
So, the task is push on button on Main Activity -> open new overlay small window and roll up Main Activity -> push on button inside a small window -> open new Activity.
In this code nothing happens after the clicking on button.
The project include main 3 classes:
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_simple_pip.setOnClickListener {
checkDrawOverlayPermission(IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE)
finishAffinity()
}
}
private fun checkDrawOverlayPermission(code: Int) {
if (Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
startActivityForResult(intent, IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE)
} else {
openFloatingWindow(code)
}
} else {
openFloatingWindow(code)
}
}
private fun openFloatingWindow(code: Int) {
when (code) {
IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE -> {
val intent = Intent(this, ImplementPipOverlayService::class.java)
val videoUrl =
"https://s3.amazonaws.com/data.development.momentpin.com/2019/7/3/1562152168485485-0661a550-9d83-11e9-9028-d7af09cf782e.mp4"
val notificationTitle = "Pip Overlay"
val notificationDescription = "Pip overlay description"
val notificationIcon = R.drawable.ic_launcher_foreground
val closeBtnColor = android.R.color.black
val closeBtnBgColor = android.R.color.transparent
intent.putExtra(ImplementPipOverlayService.KEY_STRING_VIDEO_URL, videoUrl)
intent.putExtra(ImplementPipOverlayService.KEY_STRING_NOTIFICATION_DESCRIPTION, notificationDescription)
intent.putExtra(ImplementPipOverlayService.KEY_STRING_NOTIFICATION_TITLE, notificationTitle)
intent.putExtra(ImplementPipOverlayService.KEY_INT_NOTIFICATION_ICON, notificationIcon)
intent.putExtra(ImplementPipOverlayService.KEY_INT_CLOSE_BUTTON_COLOR, closeBtnColor)
intent.putExtra(ImplementPipOverlayService.KEY_INT_CLOSE_BUTTON_BG_COLOR, closeBtnBgColor)
ContextCompat.startForegroundService(this, intent)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE, RESIZEABLE_CUSTOM_WEB_OVERLAY_REQUEST_CODE,
PIP_OVERLAY_REQUEST_CODE, RESIZEABLE_CUSTOM_VIDEO_OVERLAY_REQUEST_CODE -> {
if (Build.VERSION.SDK_INT >= 23) {
if (Settings.canDrawOverlays(this)) {
openFloatingWindow(requestCode)
}
} else {
openFloatingWindow(requestCode)
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
companion object {
const val IMPLEMENTED_PIP_OVERLAY_REQUEST_CODE = 251
const val PIP_OVERLAY_REQUEST_CODE = 252
const val RESIZEABLE_CUSTOM_VIDEO_OVERLAY_REQUEST_CODE = 253
const val RESIZEABLE_CUSTOM_WEB_OVERLAY_REQUEST_CODE = 254
}
ImplementPipOverlayService
class ImplementPipOverlayService : PipOverlayService() {
var videoUrl: String? = null
var notificationTitle: String? = null
var notificationDescription: String? = null
var notificationIcon: Int? = null
var closeButtonColor: Int? = null
var closeButtonBg: Int? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
videoUrl = intent?.getStringExtra(KEY_STRING_VIDEO_URL)
notificationTitle = intent?.getStringExtra(KEY_STRING_NOTIFICATION_TITLE)
notificationDescription = intent?.getStringExtra(KEY_STRING_NOTIFICATION_DESCRIPTION)
notificationIcon = intent?.getIntExtra(KEY_INT_NOTIFICATION_ICON, -1)
closeButtonColor = intent?.getIntExtra(KEY_INT_CLOSE_BUTTON_COLOR, -1)
closeButtonBg = intent?.getIntExtra(KEY_INT_CLOSE_BUTTON_BG_COLOR, -1)
return super.onStartCommand(intent, flags, startId)
}
override fun getForegroundNotification(): Notification {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel("my_service", "My Background Service")
} else {
packageName
}
var notificationBuilder = NotificationCompat.Builder(this, channelId)
notificationBuilder = notificationTitle?.let {
notificationBuilder.setContentTitle(it)
} ?: run {
notificationBuilder.setContentTitle("Title")
}
notificationDescription?.let {
notificationBuilder = notificationBuilder.setContentText(it)
}
notificationIcon?.let {
notificationBuilder = notificationBuilder.setSmallIcon(it)
}
val notification: Notification = notificationBuilder.build()
return notification
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String {
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
}
override fun getInitialWindowSize(): Point {
return Point(100.toDp(), 100.toDp())
}
override fun getCustomLayoutId(): Int {
return R.layout.pip_layout
}
override fun onServiceRun() {
setOnEventListener(onFullscreen = {
// Not implemented
}, onClosed = {
// Not implemented
})
pipView.removeFullscreenButton()
closeButtonColor?.let {
pipView.getCloseButton().setColorFilter(it)
}
closeButtonBg?.let {
pipView.getCloseButton().setBackgroundColor(it)
}
}
companion object {
const val KEY_STRING_VIDEO_URL = "video_url"
const val KEY_INT_CLOSE_BUTTON_COLOR = "close_button_color"
const val KEY_INT_CLOSE_BUTTON_BG_COLOR = "close_button_background"
const val KEY_STRING_NOTIFICATION_TITLE = "notification_title"
const val KEY_STRING_NOTIFICATION_DESCRIPTION = "notification_description"
const val KEY_INT_NOTIFICATION_ICON = "notification_icon"
}
OverlayPipCustomView
class OverlayPipCustomView : FrameLayout {
private lateinit var constraintsRoot: ConstraintLayout
private lateinit var imageFullscreenButton: ImageView
private lateinit var imageCloseButton: ImageView
private lateinit var customLayoutContent: FrameLayout
private lateinit var customView: View
private lateinit var touchView: View
private lateinit var button: Button
private var playerViewSize: Int = 0
private var sizeChangeable: Boolean = true
private var playerType: Int = 0
private var haveFullscreen = true
/**
* Tracks if viewResizeable is fullscreen.
*/
private var fullscreenOn: Boolean = false
val isDraggable: Boolean
get() {
return !fullscreenOn
}
private var onFullscreen: () -> Unit = {}
private var onClosed: () -> Unit = {}
private var canHideActionButtons = true
private val hideActionHandler = Handler()
private val HIDE_ACTION_DURATION = 2000L
private val hideActionRunnable = Runnable {
if (!isMoving) {
hideActions()
}
}
var isMoving: Boolean = false
constructor(ctx: Context) : super(ctx) {
inflate(context, R.layout.layout_pip_custom_view, this)
initView()
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
setAttributes(attrs)
inflate(context, R.layout.layout_pip_custom_view, this)
initView()
}
constructor(ctx: Context, attrs: AttributeSet, defStyle: Int) : super(ctx, attrs, defStyle) {
setAttributes(attrs)
inflate(context, R.layout.layout_pip_custom_view, this)
initView()
}
private fun setAttributes(attrs: AttributeSet) {
context.theme.obtainStyledAttributes(
attrs,
R.styleable.OverlayCustomView,
0, 0).apply {
try {
playerViewSize = getInteger(R.styleable.OverlayCustomView_ov_player_size, 0)
playerType = getInteger(R.styleable.OverlayCustomView_ov_size_changeable, 0)
sizeChangeable = getBoolean(R.styleable.OverlayCustomView_ov_size_changeable, true)
} finally {
recycle()
}
}
doOnLayout {
startHideAction()
}
}
private fun initView() {
button = findViewById(R.id.button)
constraintsRoot = findViewById(R.id.constraints_root)
imageFullscreenButton = findViewById(R.id.image_screen_action)
imageCloseButton = findViewById(R.id.image_close)
customLayoutContent = findViewById(R.id.custom_view)
touchView = findViewById(R.id.touch_view)
setListeners()
}
fun addCustomView(view: View) {
customLayoutContent.addView(view)
}
fun addCustomView(layoutId: Int) {
customView = inflate(context, layoutId, null)
customLayoutContent.addView(customView)
}
fun getCloseButton() = imageCloseButton
fun getConstraintsRoot() = constraintsRoot
fun getCustomLayoutContent() = customLayoutContent
fun getCustomView() = customView
fun getFullscreenButton() = imageFullscreenButton
fun getTouchView() = touchView
fun removeFullscreenButton() {
haveFullscreen = false
imageFullscreenButton.invisible()
}
private fun setListeners() {
imageFullscreenButton.setOnClickListener {
onFullscreen.invoke()
}
button.setOnClickListener{
println("pressed")
fun alert(context: Context, text: String) {
val intent = Intent(context, MainActivity2::class.java)
intent.putExtra("text", text)
context.startActivity(intent)
}
}
imageCloseButton.setOnClickListener {
onClosed.invoke()
}
}
fun setOnEventActionListener(
onFullscreen: () -> Unit,
onClosed: () -> Unit
) {
this.onFullscreen = onFullscreen
this.onClosed = onClosed
}
private fun startHideAction() {
if (canHideActionButtons) {
hideActionHandler.postDelayed(hideActionRunnable, HIDE_ACTION_DURATION)
}
}
fun restartHideAction() {
hideActionHandler.removeCallbacks(hideActionRunnable)
if (canHideActionButtons) {
hideActionHandler.postDelayed(hideActionRunnable, HIDE_ACTION_DURATION)
}
}
fun hideActions() {
if (canHideActionButtons) {
imageCloseButton.invisible()
if (haveFullscreen) {
imageFullscreenButton.invisible()
}
}
}
fun showActions() {
imageCloseButton.visible()
if (haveFullscreen) {
imageFullscreenButton.visible()
}
}
}
In third class I can't understand what should be placed here to to change initial first Activity to another one
button.setOnClickListener{
println("pressed")
fun alert(context: Context, text: String) {
val intent = Intent(context, MainActivity2::class.java)
intent.putExtra("text", text)
context.startActivity(intent)
}
}
startService(intent) will try and start a new Service and unfortunately does not throw an error if you call it with an Activity class.
To launch MainActivity2 instead call context.startActivity(intent).

setProgressAsync in ListenableWorker not sending update to LiveData

I have to implement a library that creates it's own thread to do the execution. Now, have to start the library-execution in background and hence using ListenableWorker.
But, setProgressAsync is not updating the LiveData.
Here is the code,
class ForegroundWorker(appContext: Context, params: WorkerParameters) :
ListenableWorker(appContext, params) {
private val notificationManager = appContext.getSystemService(NotificationManager::class.java)
override fun startWork(): ListenableFuture<Result> {
Log.d(TAG, "Start job")
return CallbackToFutureAdapter.getFuture { completer: CallbackToFutureAdapter.Completer<Result> ->
val callback: AsyncCallback = object : AsyncCallback {
var successes: Int = 0
override fun onError(failure: String?) {
completer.setException(Throwable())
}
override fun onSuccess(foo: String?) {
++successes
setProgressAsync(workDataOf(Progress to successes))
//completer.set(Result.success(workDataOf(Progress to successes)))
if (successes == 100) {
completer.set(Result.success(workDataOf(Progress to successes)))
}
}
}
//completer.addCancellationListener(cancelDownloadsRunnable, executor)
//for (i in 0..99) {
downloadAsynchronously("https://www.google.com", callback)
//}
callback
}
/*createNotificationChannel()
val notification = NotificationCompat.Builder(applicationContext, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Important background job")
.build()
val foregroundInfo = ForegroundInfo(NOTIFICATION_ID, notification)
setForegroundAsync(foregroundInfo)
for (i in 0..100) {
setProgressAsync(workDataOf(Progress to i))
showProgress(i)
GlobalScope.launch {
delay(delayDuration)
}
}
Log.d(TAG, "Finish job")
return Result.success()*/
}
private fun downloadAsynchronously(s: String, callback: AsyncCallback) {
createNotificationChannel()
val notification = NotificationCompat.Builder(applicationContext, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Important background job")
.build()
val foregroundInfo = ForegroundInfo(NOTIFICATION_ID, notification)
setForegroundAsync(foregroundInfo)
for (i in 0..1000) {
setProgressAsync(workDataOf(Progress to i))
showProgress(i)
GlobalScope.launch {
delay(delayDuration)
}
callback.onSuccess("")
}
}
private fun showProgress(progress: Int) {
val notification = NotificationCompat.Builder(applicationContext,
channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Important background job")
.setProgress(100, progress, false)
.build()
notificationManager?.notify(NOTIFICATION_ID, notification)
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var notificationChannel =
notificationManager?.getNotificationChannel(channelId)
if (notificationChannel == null) {
notificationChannel = NotificationChannel(
channelId, TAG, NotificationManager.IMPORTANCE_LOW
)
notificationManager?.createNotificationChannel(notificationChannel)
}
}
}
companion object {
const val NOTIFICATION_ID = 42
const val TAG = "ForegroundWorker"
const val channelId = "Job progress"
const val Progress = "Progress"
private const val delayDuration = 100L
}
interface AsyncCallback {
fun onSuccess(foo: String?)
fun onError(failure: String?)
}
}
and the livedata,
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
val workManager = WorkManager.getInstance(this)
var workRequest: OneTimeWorkRequest = OneTimeWorkRequest.from(ForegroundWorker::class.java)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
/*val workManager = WorkManager.getInstance(this)
val workRequest = OneTimeWorkRequest.from(ForegroundWorker::class.java)*/
// if (SharedPreferenceHelper.workRequestId != "") {
workManager.getWorkInfoByIdLiveData(workRequest.id)
.observe(this, Observer { workInfo: WorkInfo? ->
if (workInfo != null) {
val progress = workInfo.progress
val value = progress.getInt(Progress, 0)
binding.progressBar.progress = value
}
})
//}
workManager.enqueue(workRequest)
}
}
For some reason, setProgressAsync is not updating the Livedata.

Service does not work when the screen is off

I have a function which is responsible for detecting my location along with the location of some records that I have in the realm of my database. I calculate a range of approximately 30 meters. If my location is close to the range, it will be saved as a record in another Realm class.
All good until there, but when the phone's screen is turned off, it stops working.
I don't know how to make it work, I need to keep recording while the screen is off. I use a timer in the service. I was investigating what WorkManager is, but I don't think that's what I need.
Can you help me how to improve my code?
Thank you!
class DistanceService : Service() {
private val timer = Timer()
override fun onBind(intent: Intent): IBinder? {
Log.i("service", "Close DistanceService2")
return null
}
override fun onCreate() {
Log.i("service", "Iniciando DistanceService")
super.onCreate()
}
private fun startService() {
stopService(Intent(this, AlertRepartoSleepService::class.java))
timer.scheduleAtFixedRate(mainTask(), 0, 2000)
}
private inner class mainTask : TimerTask() {
override fun run() {
toastHandler.sendEmptyMessage(0)
}
}
private val toastHandler = #SuppressLint("HandlerLeak")
object : Handler() {
override fun handleMessage(msg: Message) {
val gps = Gps(this#DistanceService)
if (gps.isLocationEnabled()) {
if (gps.latitude.toString() != "0.0" || gps.longitude.toString() != "0.0") {
distance(this#DistanceService, gps.latitude.toString(), gps.longitude.toString())
val toast = Toast.makeText(applicationContext, "Rango SuministroReparto", Toast.LENGTH_SHORT)
toast.setGravity(Gravity.TOP, Gravity.CENTER, 0)
toast.show()
} else {
Util.toastMensaje(applicationContext, "Gps Sin Cobertura")
}
}
}
}
override fun onDestroy() {
timer.cancel()
Log.i("service", "Close DistanceService")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startService()
return START_STICKY
}
private fun distance(context: Context, latitud: String, longitud: String) {
Completable.fromAction {
Realm.getDefaultInstance().use { realm ->
val suministroImp: SuministroImplementation = SuministroOver(realm)
val registroImp: RegistroImplementation = RegistroOver(realm)
val result: RealmResults<SuministroReparto>? = suministroImp.getSuministroReparto(1)
if (result != null) {
if (result.size != 0) {
for (r: SuministroReparto in result) {
if (r.latitud.isNotEmpty() && r.longitud.isNotEmpty()) {
val l1 = Location("location 1")
l1.latitude = r.latitud.toDouble()
l1.longitude = r.longitud.toDouble()
val l2 = Location("location 2")
l2.latitude = latitud.toDouble()
l2.longitude = longitud.toDouble()
try {
val distancia = calculationByDistance(l1, l2)
if (distancia <= 15) {
if (r.foto_Reparto != 0) {
val repartoId = registroImp.getRegistroIdentity()
if (r.foto_Reparto != 2) {
suministroImp.repartoSaveService(registroImp.getRegistroIdentity(), r.id_Reparto, r.id_Operario_Reparto, Util.getFechaActual(), latitud, longitud, r.id_observacion.toString(), 0)
}
context.startService(Intent(context, AlertRepartoSleepService::class.java)
.putExtra("Cod_Orden_Reparto", r.Cod_Orden_Reparto)
.putExtra("id_cab_Reparto", r.id_Reparto)
.putExtra("direction", r.Direccion_Reparto)
.putExtra("suministroNumeroReparto", r.Suministro_Numero_reparto)
.putExtra("foto", r.foto_Reparto)
.putExtra("operarioId", r.id_Operario_Reparto)
.putExtra("cliente", r.Cliente_Reparto)
.putExtra("registroId", repartoId))
stopSelf()
break
} else {
suministroImp.repartoSaveService(registroImp.getRegistroIdentity(), r.id_Reparto, r.id_Operario_Reparto, Util.getFechaActual(), latitud, longitud, r.id_observacion.toString(), 1)
}
}
} catch (e: Exception) {
Util.toastMensaje(applicationContext, e.toString())
}
}
}
} else {
notification(this#DistanceService)
}
}
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CompletableObserver {
override fun onComplete() {
Log.i("TAG", "COMPLETADO")
}
override fun onSubscribe(d: Disposable) {
}
override fun onError(e: Throwable) {
Log.i("TAG", e.toString())
}
})
}
private fun calculationByDistance(StartP: Location, EndP: Location): Double {
val radius = 6371 * 1000
val lat1 = StartP.latitude
val lat2 = EndP.latitude
val lon1 = StartP.longitude
val lon2 = EndP.longitude
val dLat = Math.toRadians(lat2 - lat1)
val dLon = Math.toRadians(lon2 - lon1)
val a = sin(dLat / 2) * sin(dLat / 2) + (cos(Math.toRadians(lat1))
* cos(Math.toRadians(lat2)) * sin(dLon / 2)
* sin(dLon / 2))
val c = 2 * asin(sqrt(a))
val valueResult = radius * c
val m = valueResult / 1
val newFormat = DecimalFormat("####")
val mInDec = Integer.valueOf(newFormat.format(m))
return mInDec.toDouble()
}
private fun getBasicNotificationBuilder(context: Context, channelId: String, playSound: Boolean)
: NotificationCompat.Builder {
val notificationSound: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val nBuilder = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
.setAutoCancel(true)
.setDefaults(0)
if (playSound) nBuilder.setSound(notificationSound)
return nBuilder
}
private fun notification(context: Context) {
val intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val nBuilder = getBasicNotificationBuilder(context, CHANNEL_ID_TIMER, false)
nBuilder.setContentTitle(String.format("Reparto"))
.setContentText("Acabas de culminar tus repartos . Tomar selfie FIN DE TRABAJO")
.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
.setAutoCancel(true)
.setContentIntent(pendingIntent)
val nManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nManager.createNotificationChannel(CHANNEL_ID_TIMER, CHANNEL_NAME_TIMER, true)
nManager.notify(1, nBuilder.build())
}
#TargetApi(26)
private fun NotificationManager.createNotificationChannel(channelID: String,
channelName: String,
playSound: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelImportance = if (playSound) NotificationManager.IMPORTANCE_DEFAULT
else NotificationManager.IMPORTANCE_LOW
val nChannel = NotificationChannel(channelID, channelName, channelImportance)
nChannel.enableLights(true)
nChannel.lightColor = Color.BLUE
this.createNotificationChannel(nChannel)
}
}
companion object {
private const val CHANNEL_ID_TIMER = "enable_gps"
private const val CHANNEL_NAME_TIMER = "Dsige_Enable_Gps"
private const val TIMER_ID = 0
}
}

Categories

Resources