This question already has answers here:
Context.startForegroundService() did not then call Service.startForeground()
(33 answers)
Closed 1 year ago.
I am trying to call startForegroundService(intent) but my app crashes after few seconds but I am able to call startForegroundService(intent) in my other activity it works fine and both the activities have the same code. I am not able to figure out what is causing this problem. I am trying to upload some photos in activity one it's working without any issues and in this activity it's crashing the app after few seconds I click on the button
Stack Trace
2021-07-18 22:48:16.233 8352-8352/com.android.testproject1 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.testproject1, PID: 8352
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{dac122 u0 com.android.testproject1/.services.UploadServiceOffers}
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2005)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
My Code
Activity
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId==R.id.Post){
if (descriptionTextTitle.text.isEmpty()) {
// AnimationUtil.shakeView(mEditText, activity)
} else {
sharedPreferences.edit().putInt("count", ++serviceCount).apply()
Log.d(myTag, "On click sp $serviceCount")
val intent = Intent(this, UploadServiceOffers::class.java)
intent.putExtra("count", serviceCount)
intent.putExtra("notification_id", System.currentTimeMillis().toInt())
intent.action = UploadServiceOffers.ACTION_START_FOREGROUND_SERVICE_UPLOAD_OFFERS
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
Log.d(myTag, "Build Version OP")
// startForegroundService(activity!!,intent)
} else {
Log.d(myTag, "Build Version NP")
// activity!!.startService(intent)
startService(intent)
}
Toasty.info(this, "Uploading images..", Toasty.LENGTH_SHORT, true).show()
finish()
}
}
return super.onOptionsItemSelected(item)
}
Service
class UploadServiceOffers : Service() {
companion object{
val ACTION_START_FOREGROUND_SERVICE_UPLOAD_OFFERS = "ACTION_START_FOREGROUND_SERVICE"
}
private var count = 0
private var bitmap: Bitmap? = null
private var resized: Bitmap? = null
val myTag:String = "MyTag"
override fun onCreate() {
Log.d(myTag, "Service Created ")
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(myTag, "onStart $intent $flags $startId")
if (intent!=null) {
val action = intent.action
if (action == ACTION_START_FOREGROUND_SERVICE_UPLOAD_OFFERS) {
val imagesList: ArrayList<Image>? = intent.getParcelableArrayListExtra<Image>("imagesList")
val notificationId = intent.getIntExtra("notification_id", 3)
val postID = intent.getStringExtra("offerID")
val title=intent.getStringExtra("title")
val originalPrice=intent.getStringExtra("originalPrice")
val discountedPrice=intent.getStringExtra("discountedPrice")
val city=intent.getStringExtra("city")
val currentId = intent.getStringExtra("current_id")
val description = intent.getStringExtra("description")
val uploadedImagesUrl = intent.getStringArrayListExtra("uploadedImagesUrl")
count = intent.getIntExtra("count", 0)
if (imagesList != null) {
if (postID != null) {
if (title != null) {
if (city != null) {
if (originalPrice != null) {
if (discountedPrice != null) {
uploadImages(notificationId, 0, imagesList, currentId, description,
uploadedImagesUrl, postID,title,originalPrice,discountedPrice,city)
}
}
}
}
}
}
}
}
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
private fun stopForegroundService(removeNotification: Boolean) {
Log.d(myTag,"Stop foreground service.")
// Stop foreground service and remove the notification.
stopForeground(removeNotification)
// Stop the foreground service.
stopSelf()
}
private fun notifyProgress(
id: Int,
icon: Int,
title: String,
message: String,
context: Context,
max_progress: Int,
progress: Int,
indeterminate: Boolean
) {
val builder = NotificationCompat.Builder(context, App.CHANNEL_ID2)
// Create notification default intent.
val intent = Intent()
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
builder.setSmallIcon(icon)
.setContentTitle(title)
.setContentText(message)
.setOngoing(true)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setTicker(message)
.setChannelId(App.CHANNEL_ID2)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setProgress(max_progress, progress, indeterminate)
.setVibrate(LongArray(0))
startForeground(id, builder.build())
}
fun getImageUri(inContext: Context, inImage: Bitmap) {
val bytes = ByteArrayOutputStream()
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes)
var path: String?=null
try {
path = MediaStore.Images.Media.insertImage(inContext.contentResolver, inImage, "Title", null)
}catch (e: java.lang.Exception){
e.localizedMessage?.toString()?.let { Log.d(myTag, it) }
}
if (path!=null){
Log.d(myTag, Uri.parse(path).toString())
}
else{
Log.d(myTag, "Path is null ")
}
}
private fun uploadImages(
notification_id: Int,
index: Int,
imagesList: ArrayList<Image>,
currentUser_id: String?,
description: String?,
uploadedImagesUrl: ArrayList<String>?,
postID:String,
title: String,
originalPrice:String,
discountPrice:String,
city:String
) {
val imgCount = index + 1
var imageUri: Uri
val imageUri0: Uri?= Uri.fromFile(File(imagesList[index].path))
if (Build.VERSION.SDK_INT >= 29) {
try {
bitmap = imageUri0?.let { ImageDecoder.createSource(this.contentResolver,it)}?.let { ImageDecoder.decodeBitmap(it) }
} catch (e: IOException) {
e.printStackTrace()
e.localizedMessage?.toString()?.let { Log.d(myTag, " errore is $it") }
}
} else {
// Use older version
try {
bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, imageUri0)
} catch (e: IOException) {
e.printStackTrace()
e.localizedMessage?.toString()?.let { Log.d(myTag, " errore is $it") }
}
}
// val bitmap = BitmapFactory.decodeFile(file.getAbsolutePath())
resized = bitmap?.let { Bitmap.createScaledBitmap(it, 600, 600, true) }
// Log.d(myTag, "path is ${bitmap.toString()}")
var path :String?=null
try {
// path = MediaStore.Images.Media.insertImage(this.contentResolver, resized, "Title", null)
path = MediaStore.Images.Media.insertImage(this.contentResolver, resized, "IMG_"
+ System.currentTimeMillis(), null)
Log.d(myTag, "path is $path")
}catch (e :java.lang.Exception){
Log.d(myTag, "path is exception $path" )
Log.d(myTag, e.localizedMessage.toString() )
}
imageUri = Uri.parse(path)
// imageUri = try {
//
// val compressedFile: File = id.zelory.compressor.Compressor()
// .setQuality(80)
// .setCompressFormat(Bitmap.CompressFormat.JPEG)
// .compressToFile(File(imagesList[index].path))
// Uri.fromFile(compressedFile)
// } catch (e: Exception) {
// e.printStackTrace()
// Uri.fromFile(File(imagesList!![index].path))
// }
val fileToUpload =
currentUser_id?.let {
FirebaseStorage.getInstance().reference.child("Offers").child(it)
.child(postID)
.child("Voila_"+ System.currentTimeMillis() + "_" + imagesList[index].name)
}
fileToUpload?.putFile(imageUri)?.addOnSuccessListener {
Log.d(myTag, "Uploaded Successfully")
fileToUpload.downloadUrl
.addOnSuccessListener { uri: Uri ->
uploadedImagesUrl!!.add(uri.toString())
val nextIndex = index + 1
try {
if (!TextUtils.isEmpty(imagesList[index + 1].path)) {
uploadImages(
notification_id,
nextIndex,
imagesList,
currentUser_id,
description,
uploadedImagesUrl,
postID,
title,originalPrice, discountPrice,city)
} else {
uploadPost(
notification_id,
currentUser_id,
description,
uploadedImagesUrl,
postID,
title,originalPrice,discountPrice,city)
}
} catch (e: Exception) {
e.printStackTrace()
uploadPost(
notification_id,
currentUser_id,
description,
uploadedImagesUrl, postID,
title, originalPrice, discountPrice,city)
}
}
.addOnFailureListener { obj: Exception -> obj.printStackTrace() }
}?.addOnFailureListener { obj: Exception ->
obj.printStackTrace()
obj.localizedMessage?.toString()?.let { Log.d(myTag, "Exception is $it") }
}?.addOnProgressListener { taskSnapshot: UploadTask.TaskSnapshot ->
if (count == 1) {
val title = "Uploading " + imgCount + "/" + imagesList.size + " images..."
val progress = (100.0 * taskSnapshot.bytesTransferred / taskSnapshot.totalByteCount).toInt()
notifyProgress(notification_id, R.drawable.stat_sys_upload, title, "$progress%",
applicationContext, 100, progress, true)
} else if (count > 1) {
notifyProgress(
notification_id,
R.drawable.stat_sys_upload,
"Viola",
"Uploading $count posts",
applicationContext,
100,
0,
true
)
}
}
}
}
In order to avoid crashing your app, you must call startForeground(notification) inside your onStartCommand method, to show notification immediately, or as soon as the service is started.
Check the information here
The new Context.startForegroundService() method starts a foreground service. The system allows apps to call Context.startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method within five seconds after the service is created.
Related
I get this issue from firebase, from a moto g5, 100% in background android 8.1, im running a foreground service, here is the firebase stacktrace
Fatal Exception: java.util.ConcurrentModificationException
at android.util.ArrayMap.put(ArrayMap.java:523)
at android.os.Bundle.putParcelable(Bundle.java:501)
at android.app.Notification.addFieldsFromContext(Notification.java:2440)
at android.app.Notification.addFieldsFromContext(Notification.java:2433)
at android.app.Notification$Builder.build(Notification.java:4974)
at com.apep.burnedcalories.CountUpTimerService.updateNotification(CountUpTimerService.java:445)
at com.apep.burnedcalories.CountUpTimerService.access$updateNotification(CountUpTimerService.java:43)
at com.apep.burnedcalories.CountUpTimerService$onStartCommand$1.invokeSuspend(CountUpTimerService.kt:335)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
this is the foreground service class onstartcomand method, here i call the updatenotification method every second to update the title because its a timer:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
CoroutineScope(job).launch(Dispatchers.IO) {
while (isBound) {
if (!isBound) break
if(timer_stoped == false){
startChronometer()
timeFlow.value = getTimestamp().also { time ->
if(start_exercise.txt_time2 != time) {
if(mets_i != intervalo_mets || peso != intervalo_peso){
intervalo = elapsedmilis
intervalo_kcal = burnedcalories
intervalo_mets = mets_i
intervalo_peso = peso
}
burnedcalories = intervalo_kcal + (((0.0175 / 60) * mets_i * peso)*((elapsedmilis-intervalo)/1000))
burnedfat = (burnedcalories / 7.7)
if(isguided == true){
if(progress < 100.0){
if(tts_init == true){
tts_init = false
if (lang_supported == true) {
speak_start(remaining_time)
} else speak_start_en(remaining_time)
}
progress = ((burnedcalories-resume_kcal.toDouble())/start_exercise.goalkcal)*100
remaining_time = getTimestamp2(((((start_exercise.goalkcal+resume_kcal.toDouble())- burnedcalories)/((0.0175/60)*start_exercise.mets_i*start_exercise.peso))*1000).toLong())
}else{
progress = 100.0
remaining_time = "00:00:00"
}
if(((100/numero_notis)*notis_done)<=progress.toInt()){
if(numero_notis == notis_done){
if (lang_supported == true) {
speak_end()
} else speak_end_en()
}else{
if (lang_supported == true) {
speak_noti(burnedcalories.toInt().toString(), progress.toInt().toString())
speak_start(remaining_time)
} else{
speak_noti_en(burnedcalories.toInt().toString(), progress.toInt().toString())
speak_start_en(remaining_time)
}
}
notis_done++
val prefs = getSharedPreferences("prefs", MODE_PRIVATE)
val editor = prefs.edit()
editor.putString("burnedcalories", burnedcalories.toString())
editor.putLong("elapsedmilis", elapsedmilis)
editor.putInt("notis_done", notis_done)
editor.apply()
}
}
start_exercise.txt_time2 = time
start_exercise.txt_time = time
updateNotification(time, start_exercise.burnedcalories.toInt().toString(), start_exercise.burnedfat.toInt().toString())
}
}
}else{
pauseChronometer()
}
delay(1000)
}
}
return START_STICKY
}
This is the updatenotification method, here is where i get the exeption:
private fun updateNotification(timestamp: String, caloriasquemadas: String, grasaquemada: String) {
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val notification = notificationBuilder
.setContentTitle(timestamp)
.setContentText(caloriasquemadas + "KCAL - " + grasaquemada + "G")
.build()
notificationManager.notify(Constants.NOTIFICATION_ID, notification)
}else{
val notification = builder
.setContentTitle(timestamp)
.setContentText(caloriasquemadas + "KCAL - " + grasaquemada + "G")
.build()
notificationManager.notify(Constants.NOTIFICATION_ID, notification)
}
}
I am using the Kotlin in this timer app trying to pause the timer I get this error. It's not running the first if statement that is true.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.proITAssets.myapplication, PID: 12792
java.lang.IllegalStateException
at android.media.MediaRecorder.pause(Native Method)
at com.proITAssets.myapplication.MainActivity.pauseRecording(MainActivity.kt:143)
at com.proITAssets.myapplication.MainActivity.access$pauseRecording(MainActivity.kt:23)
at com.proITAssets.myapplication.MainActivity$onCreate$3.onClick(MainActivity.kt:87)
at android.view.View.performClick(View.java:7125)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
This is a code snippet for the pause function.
#SuppressLint("RestrictedApi", "SetTextI18n")
#TargetApi(Build.VERSION_CODES.N)
private fun pauseRecording() {
if(state) {
pauseTimer()
Toast.makeText(this,"This is working", Toast.LENGTH_SHORT).show()
if(!recordingStopped){
Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
mediaRecorder?.pause()
recordingStopped == true
button_pause_recording.text = "Resume"
}else{
resumeRecording()
resumeTimer()
}
}else{
Toast.makeText(this,"this is not working", Toast.LENGTH_SHORT).show()
}
}
This is all the code that the program used to run please let me know where the problem may be.
class MainActivity : AppCompatActivity() {
private var output: String? = null
private var mediaRecorder: MediaRecorder? = null
private var state: Boolean = false
private var recordingStopped: Boolean = false
private var mp= MediaPlayer()
private var recordingfile = ""
private var chronometerPausedTime: Long = 0
private val thename by lazy {findViewById<TextView>(R.id.ed_name)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val name = "$thename".trim()
mediaRecorder = MediaRecorder()
output = Environment.getExternalStorageDirectory().toString() + name
button_stop_recording.isEnabled = false
button_pause_recording.isEnabled = false
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
val permissions = arrayOf(
android.Manifest.permission.RECORD_AUDIO,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
ActivityCompat.requestPermissions(this, permissions, 0)
}
button_start_recording.setOnClickListener {
if (!thename.text.toString().isNotEmpty()){
Toast.makeText(this, "User need to enter the file name", Toast.LENGTH_SHORT).show()
}else{
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
ActivityCompat.requestPermissions(this, permissions,0)
} else {
savefilename()
startRecording()
button_stop_recording.isEnabled = true
button_start_recording.isEnabled = false
button_pause_recording.isEnabled = true
}
savefilename()
}
}
mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
mediaRecorder?.setOutputFile(output)
button_stop_recording.setOnClickListener{
stopRecording()
button_start_recording.isEnabled = true
button_stop_recording.isEnabled = false
}
button_pause_recording.setOnClickListener {
pauseRecording()
button_start_recording.isEnabled = true
}
btn_play.setOnClickListener {
playRecording()
}
}
private fun startRecording() {
startFromZero()
state = true
if (!state){
Toast.makeText(this, "This is false", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(this, "this is true", Toast.LENGTH_SHORT).show()
}
var name = File(getExternalFilesDir("/"),recordingfile)
//val name = Activity()?.getExternalFilesDir("/")?.absolutePath
val timestamp = SimpleDateFormat(
"MM/dd/yyyy_HH:mm:ss:a",
Locale.US,
).format(System.currentTimeMillis())
val date = Date()
recordingfile = "${ed_name.text}" + timestamp.format(date) + ".mp3"
try {
if (name != null) {
Toast.makeText(this, "This is your file name $recordingfile", Toast.LENGTH_SHORT).show()
}
mediaRecorder?.setOutputFile("/" + name + recordingfile)
mediaRecorder?.prepare()
mediaRecorder?.start()
Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show()
} catch (e: IllegalStateException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
#SuppressLint("RestrictedApi", "SetTextI18n")
#TargetApi(Build.VERSION_CODES.N)
private fun pauseRecording() {
if(state) {
pauseTimer()
Toast.makeText(this,"This is working", Toast.LENGTH_SHORT).show()
if(!recordingStopped){
Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
mediaRecorder?.pause()
recordingStopped == true
button_pause_recording.text = "Resume"
}else{
resumeRecording()
resumeTimer()
}
}else{
Toast.makeText(this,"this is not working", Toast.LENGTH_SHORT).show()
}
}
#SuppressLint("RestrictedApi", "SetTextI18n")
#TargetApi(Build.VERSION_CODES.N)
private fun resumeRecording() {
Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
mediaRecorder?.resume()
button_pause_recording.text = "Pause"
recordingStopped == false
}
private fun stopRecording(){
stopTimer()
if(state){
mediaRecorder?.stop()
mediaRecorder?.release()
state = false
simpleChronometer.stop()
}else{
Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
}
}
private fun playRecording(){
mp.setDataSource(output)
mp.prepare()
mp.start()
}
private fun savefilename(){
val name = "rec_$thename.mp3"
val timestamp = SimpleDateFormat("MMddyyyy_HHmmss",
Locale.getDefault()).format(System.currentTimeMillis())
try {
val rec = File("$output/Recording/")
mediaRecorder = MediaRecorder()
output = Environment.getExternalStorageDirectory().toString() + "/$thename.mp3"
val file = File(name)
val fw = FileWriter(file.absoluteFile)
val bw = BufferedWriter(fw)
bw.write(name)
bw.close()
Toast.makeText(this, name +"is name of the file \n" + rec, Toast.LENGTH_LONG).show()
}catch (e: Exception){
Toast.makeText(this, e.message, Toast.LENGTH_SHORT )
}
}
private fun startFromZero() {
simpleChronometer.base = SystemClock.elapsedRealtime()
simpleChronometer.start()
}
private fun pauseTimer() {
chronometerPausedTime = SystemClock.elapsedRealtime() - this.simpleChronometer.base
simpleChronometer.stop()
}
private fun resumeTimer() {
simpleChronometer.base = SystemClock.elapsedRealtime() - chronometerPausedTime
chronometerPausedTime = 0
simpleChronometer.start()
}
private fun stopTimer(){
simpleChronometer.base = SystemClock.elapsedRealtime()
simpleChronometer.stop()
}
}
Thank you for the help.
This IllegalStateException is raised because you press the pause button (i.e. mediaRecorder?.pause()) before start recording with mediaRecorder?.start()
The cause of that is in the if(!recordingStopped) conditional:
if(!recordingStopped){
Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
mediaRecorder?.pause()
recordingStopped == true
The initial value of recordingStopped is false, so this condition is met even before start recording, and hence the exception will be raised.
So the initial value should be true:
private var recordingStopped: Boolean = true
UPDATE:
You need to use a single = sign within the resumeRecording() & pauseRecording() instead of ==:
#SuppressLint("RestrictedApi", "SetTextI18n")
#TargetApi(Build.VERSION_CODES.N)
private fun resumeRecording() {
Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
mediaRecorder?.resume()
button_pause_recording.text = "Pause"
recordingStopped = false // <<<< FIX HERE
}
private fun pauseRecording() {
if(state) {
pauseTimer()
Toast.makeText(this,"This is working", Toast.LENGTH_SHORT).show()
if(!recordingStopped){
Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
mediaRecorder?.pause()
recordingStopped = true // <<<< FIX HERE
button_pause_recording.text = "Resume"
}else{
resumeRecording()
resumeTimer()
}
}else{
Toast.makeText(this,"this is not working", Toast.LENGTH_SHORT).show()
}
}
In stopRecording(), the recordingStopped value should be updated:
private fun stopRecording(){
stopTimer()
if(state){
mediaRecorder?.stop()
mediaRecorder?.release()
state = false
simpleChronometer.stop()
recordingStopped = true // <<<<<<< UPDATE HERE
}else{
Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
}
}
I know this:
A broadcast receiver is ALWAYS called because It sets the alarm for the next day.
When It works if the App is in Foreground, or if it is closed or if it is closed screen.
I use 'com.github.thefuntasty.hauler:library:2.0.0' to dismiss the activity with swipe
No other alarm is set at that time
I don't get any crash reports
I have NO idea, why the activity doesn't start. I tried doing try and catch, to see if that would help but it didn't.
EDIT:
Expected:
When the Broadcast reciever triggers, set the alarm for the next day, start the Activity in the foreground, even if the phone is locked and play the alarm.
On swipe down destroy the activity
Currently:
Sometimes it opens the activity other times it doesn't and I don't know why. If I set it for 5 min from now (locked screen, closed app), it works without problem. If I set it for tomorrow, it works 90% of the time.
I'm trying to figure out, why the activity sometimes doesn't start, because I'm not getting any crash reports from the firebase.
This is the code:
Manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver
android:name=".views.alarm.broadcast.ResumeOnBootAlarm"
android:enabled="true"
android:exported="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver
android:name=".views.alarm.broadcast.MyAlarm"
android:exported="true" />
<activity
android:name=".views.alarm.broadcast.TriggeredAlarmActivity"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:theme="#style/AppTheme.Draggable" />
This is my BroadCastReciever:
class MyAlarm : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val alarmID = intent.extras?.getInt(ARG_ALARM_ID)
//Immediatly set new alarm
val realm = Realm.getDefaultInstance()
val alarmFromRealm = DataHelper.getAlarm(realm, alarmID!!)
alarmFromRealm?.let { alarm ->
val shouldEnableAlarm = alarm.isEnabled && alarm.daysList!!.isNotEmpty()
DataHelper.enableAlarm(alarmID, shouldEnableAlarm, realm)
if (shouldEnableAlarm) {
setAlarm(context, alarm, false)
} else {
cancelAlarm(context, alarm.id)
}
}
try {
context.startActivity(TriggeredAlarmActivity.getIntent(context, alarmID))
} catch (ex: Exception) {
Crashlytics.logException(ex)
context.startActivity(TriggeredAlarmActivity.getIntent(MyApplication.appContext, null))
}
}
}
And this is the TriggeredActivity:
class TriggeredAlarmActivity : BaseActivity() {
private var currentUserVolume: Int = 0
private lateinit var timer: Timer
private lateinit var realm: Realm
private lateinit var vibrator: Vibrator
private var mediaPlayer: MediaPlayer? = null
private var shouldResumePlaying: Boolean = false
private val alarmID: Int?
get() {
return intent.extras?.getInt(ARG_ALARM_ID)
}
private val mAudioManager: AudioManager by lazy {
baseContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
}
/*
LifeCycle
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_triggered_alarm)
initTextClock()
showIfScreenIsLocked()
showDanceAnimation()
if (alarmID == null) {
val uri = getDefaultRingtone()
mediaPlayer= MediaPlayer()
mediaPlayer?.setDataSource(this,uri)
mediaPlayer?.prepare()
mediaPlayer?.start()
} else {
realm = Realm.getDefaultInstance()
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
val alarmFromRealm = DataHelper.getAlarm(realm, alarmID!!)
alarmFromRealm?.let { alarm ->
try {
if (alarm.useDefaultRingtone) {
val uri = getDefaultRingtone()
mediaPlayer = MediaPlayer()
mediaPlayer?.let { increaseVolumeOverTime(it, alarm.shouldVibrate) }
mediaPlayer?.setDataSource(this, uri)
mediaPlayer?.isLooping = true
shouldResumePlaying = alarm.shouldResumePlaying
mediaPlayer?.setOnPreparedListener {
if (alarm.shouldResumePlaying) {
mediaPlayer?.seekTo(alarm.secondsPlayed)
}
mediaPlayer?.start()
}
mediaPlayer?.prepareAsync()
} else {
initMediaPlayer(alarm)
}
} catch (exception: Exception) {
Crashlytics.logException(exception)
val uri = getDefaultRingtone()
mediaPlayer = MediaPlayer()
mediaPlayer?.let { increaseVolumeOverTime(it, true) }
mediaPlayer?.setDataSource(this, uri)
mediaPlayer?.isLooping = true
mediaPlayer?.setOnPreparedListener {
mediaPlayer?.start()
}
mediaPlayer?.prepareAsync()
}
}
}
haulerView?.setOnDragDismissedListener {
finish() // finish activity when dismissed
}
}
private fun getDefaultRingtone(): Uri {
var uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
}
}
return uri
}
private fun initTextClock() {
val period = 5000L
val timer = Timer()
val formatter = SimpleDateFormat("HH:mm")
val task = object : TimerTask() {
override fun run() {
val today = Date()
runOnUiThread {
tvCurrentTimeActual?.text = formatter.format(today)
}
}
}
timer.scheduleAtFixedRate(task, 0L, period)
}
override fun onDestroy() {
super.onDestroy()
if (shouldResumePlaying) {
mediaPlayer?.let {
alarmID?.let { it1 -> DataHelper.updateProgress(it1, it.currentPosition) }
}
}
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentUserVolume, 0)
timer.cancel()
mediaPlayer?.stop()
vibrator.cancel()
realm.close()
}
/*
Private
*/
private fun showIfScreenIsLocked() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
} else {
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
)
}
}
private fun showDanceAnimation() {
val lottieFiles = mutableListOf(
"lottie/dance/chicken_6.json",
"lottie/dance/sound.json", //White
"lottie/dance/pinguin.json" //White
)
val file = lottieFiles.random()
messageLottie?.setAnimation(file)
if (file == "lottie/dance/pinguin.json"
|| file == "lottie/dance/sound.json"
) {
messageLottie?.addValueCallback(
KeyPath("**"), LottieProperty.COLOR_FILTER,
{ PorterDuffColorFilter(getColor(R.color.white), PorterDuff.Mode.SRC_ATOP) }
)
}
messageLottie?.playAnimation()
}
private fun increaseVolumeOverTime(mediaPlayer: MediaPlayer, shouldVibrate: Boolean) {
mediaPlayer.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
)
currentUserVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
var currentVolume = 1
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0)
if (shouldVibrate) {
startVibrating()
}
timer = Timer()
timer.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
currentVolume += 1
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0)
if (currentVolume % 10 == 0) {
if (shouldVibrate) {
startVibrating(currentVolume)
}
}
if (currentVolume >= 90) this.cancel()
}
}, 0, 2000)
}
private fun startVibrating(currentVolume: Int = 10) {
val vibratorLength = ((50 * currentVolume) / 1.2).roundToInt().toLong()
val patternShort = longArrayOf(1200, vibratorLength)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(patternShort, 0))
} else {
vibrator.vibrate(patternShort, 0)
}
}
private fun initMediaPlayer(alarm: Alarm) {
mediaPlayer = MediaPlayer()
mediaPlayer?.let { increaseVolumeOverTime(it, alarm.shouldVibrate) }
val currentlySelectedPath = alarm.currentlySelectedPath
if (currentlySelectedPath == null) {
var uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (uri == null) {
uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
}
}
mediaPlayer?.setDataSource(this, uri)
} else {
mediaPlayer?.setDataSource(this, Uri.parse(currentlySelectedPath))
}
mediaPlayer?.isLooping = false
mediaPlayer?.setOnCompletionListener {
it?.stop()
it?.reset()
it?.isLooping = false
val path = alarm.songsList?.random()?.path
it?.setDataSource(this, Uri.parse(path))
alarmID?.let { it1 -> DataHelper.nextRandomSong(it1, path) }
it?.setOnPreparedListener {
mediaPlayer?.start()
}
it?.prepareAsync()
}
mediaPlayer?.setOnPreparedListener {
if (alarm.shouldResumePlaying) {
mediaPlayer?.seekTo(alarm.secondsPlayed)
}
mediaPlayer?.start()
}
mediaPlayer?.prepareAsync()
shouldResumePlaying = alarm.shouldResumePlaying
}
companion object {
const val ARG_ALARM_ID = "AlarmID"
fun getIntent(context: Context, alarmID: Int?): Intent {
val intent = Intent(context, TriggeredAlarmActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY) //If it doesn't hide in recent use or Intent.FLAG_ACTIVITY_CLEAR_TASK
intent.putExtra(ARG_ALARM_ID, alarmID)
return intent
}
}
}
I am trying to capture the image every 1 second. I use a handler for the task. Here is the code for that part.
val handler = Handler()
button.setOnClickListener {
prepareUIForCapture()
if(isRunning){
handler.removeCallbacksAndMessages(null)
restartActivity()
}else{
button.text = "Stop"
handler.postDelayed(object : Runnable {
override fun run(){
twoLens.reset()
twoLens.isTwoLensShot = true
MainActivity.cameraParams.get(dualCamLogicalId).let {
if (it?.isOpen == true) {
Logd("In onClick. Taking Dual Cam Photo on logical camera: " + dualCamLogicalId)
takePicture(this#MainActivity, it)
Toast.makeText(applicationContext, "Captured", Toast.LENGTH_LONG).show()
}
}
handler.postDelayed(this, 1000)
}
}, 1000)
}
isRunning = !isRunning
}
}
This works as intended, however after around 3 minutes, the camera preview lags and the capture stops and eventually the application crashes.
Here is the code for takePicture()
fun takePicture(activity: MainActivity, params: CameraParams) {
if (!params.isOpen) {
return
}
try {
Logd("In captureStillPicture.")
val camera = params.captureSession?.getDevice()
if (null != camera) {
params.captureBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
// params.captureBuilder?.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
params.captureBuilder?.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO)
if (params.id.equals(dualCamLogicalId) && twoLens.isTwoLensShot) {
val normalParams: CameraParams? = MainActivity.cameraParams.get(normalLensId)
val wideParams: CameraParams? = MainActivity.cameraParams.get(wideAngleId)
if (null == normalParams || null == wideParams)
return
Logd("In captureStillPicture. This is a Dual Cam shot.")
params.captureBuilder?.addTarget(normalParams.imageReader?.surface!!)
params.captureBuilder?.addTarget(wideParams.imageReader?.surface!!)
}
params.captureBuilder?.set(CaptureRequest.JPEG_QUALITY, 50)
// Orientation
val rotation = activity.getWindowManager().getDefaultDisplay().getRotation()
var capturedImageRotation = getOrientation(params, rotation)
params.captureBuilder?.set(CaptureRequest.JPEG_ORIENTATION, capturedImageRotation)
try {
params.captureSession?.stopRepeating()
} catch (e: CameraAccessException) {
e.printStackTrace()
}
//Do the capture
if (28 <= Build.VERSION.SDK_INT )
params.captureSession?.captureSingleRequest(params.captureBuilder?.build(), params.backgroundExecutor, StillCaptureSessionCallback(activity, params))
else
params.captureSession?.capture(params.captureBuilder?.build(), StillCaptureSessionCallback(activity, params),
params.backgroundHandler)
}
} catch (e: CameraAccessException) {
e.printStackTrace()
} catch (e: IllegalStateException) {
Logd("captureStillPicture IllegalStateException, aborting: " + e)
}
}
Here is the part to create the preview.
fun createCameraPreviewSession(activity: MainActivity, camera: CameraDevice, params: CameraParams) {
Logd("In createCameraPreviewSession.")
if (!params.isOpen) {
return
}
try {
if (Build.VERSION.SDK_INT >= 28 && params.id.equals(MainActivity.dualCamLogicalId)) {
val normalParams: CameraParams? = MainActivity.cameraParams.get(normalLensId)
val wideParams: CameraParams? = MainActivity.cameraParams.get(wideAngleId)
Logd("In createCameraPreview. This is a Dual Cam stream. Starting up simultaneous streams.")
if (null == normalParams || null == wideParams)
return
// val normalTexture = normalParams.previewTextureView?.surfaceTexture
val wideTexture = wideParams.previewTextureView?.surfaceTexture
// if (null == normalTexture || null == wideTexture)
// return
// normalTexture.setDefaultBufferSize(400,400)
wideTexture!!.setDefaultBufferSize(400,400)
// val normalSurface = Surface(normalTexture)
val wideSurface = Surface(wideTexture)
val wideOutputConfigPreview = OutputConfiguration(wideSurface)
val wideOutputConfigImageReader = OutputConfiguration(wideParams.imageReader?.surface!!)
wideOutputConfigPreview.setPhysicalCameraId(wideAngleId)
wideOutputConfigImageReader.setPhysicalCameraId(wideAngleId)
// val normalOutputConfigPreview = OutputConfiguration(normalSurface)
val normalOutputConfigImageReader = OutputConfiguration(normalParams.imageReader?.surface!!)
normalOutputConfigImageReader.setPhysicalCameraId(normalLensId)
val sessionConfig = SessionConfiguration(SessionConfiguration.SESSION_REGULAR,
Arrays.asList( normalOutputConfigImageReader, wideOutputConfigPreview, wideOutputConfigImageReader),
params.backgroundExecutor, PreviewSessionStateCallback(activity, params))
params.previewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
// params.previewBuilder?.addTarget(normalSurface)
params.previewBuilder?.addTarget(wideSurface)
camera.createCaptureSession(sessionConfig)
} else {
val texture = params.previewTextureView?.surfaceTexture
if (null == texture)
return
val surface = Surface(texture)
if (null == surface)
return
params.previewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
params.previewBuilder?.addTarget(surface)
val imageSurface = params.imageReader?.surface
if (null == imageSurface)
return
if (Build.VERSION.SDK_INT >= 28) {
val sessionConfig = SessionConfiguration(SessionConfiguration.SESSION_REGULAR,
Arrays.asList(OutputConfiguration(surface), OutputConfiguration(imageSurface)),
params.backgroundExecutor, PreviewSessionStateCallback(activity, params))
camera.createCaptureSession(sessionConfig)
} else {
camera.createCaptureSession(Arrays.asList(surface, imageSurface),
PreviewSessionStateCallback(activity, params), params.backgroundHandler)
}
}
} catch (e: CameraAccessException) {
e.printStackTrace()
} catch (e: IllegalStateException) {
Logd("createCameraPreviewSession IllegalStateException, aborting: " + e)
}
}
I tried clearing the buffers and bitmaps, but the issue persists. I am thinking I might not be using the correct approach towards continuous capture. Is this way correct?
Any idea why the preview freezes?
Here is the original repo: https://github.com/google/basicbokeh
I did changes to suit my needs since it did not run on my device (HWMate20Pro) off the shelf and I needed continuous captures from both lenses.
Thanks.
I created service and firebase job service too. They work perfectly when app is closed.
I want get user's location every minute and send to server when app is closed
This is my jobservice:
class NeverEndingJob : JobService() {
var counter = 0
var TAG = "NeverEndingJOb"
private val NOTIFICATION_ID = 404
private val CHANNEL_ID = "AppC"
internal var name: CharSequence = "AppC"
internal var importance: Int = 0
internal var mChannel: NotificationChannel? = null
internal var mNotificationManager: NotificationManager? = null
private var mLocationManager: LocationManager? = null
private var owner: ICapture? = null
var model :MyRestModel? = null
var notificationManager: NotificationManagerCompat? = null
private var mLocationListeners = arrayOf(LocationListener(LocationManager.GPS_PROVIDER), LocationListener(LocationManager.NETWORK_PROVIDER))
init {
MLog.d(TAG,"job created")
}
override fun onCreate() {
super.onCreate()
mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
importance = NotificationManager.IMPORTANCE_MIN
mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mNotificationManager!!.createNotificationChannel(mChannel)
}
}
override fun onStopJob(job: JobParameters?): Boolean {
MLog.d(TAG,"job destroy")
val intent = Intent("app.name.RestartService")
sendBroadcast(intent)
stopTimer()
return true
}
override fun onStartJob(job: JobParameters?): Boolean {
mNotificationManager!!.notify(NOTIFICATION_ID, getNotification(R.string.working.getResource()))
startForeground(NOTIFICATION_ID, getNotification(R.string.working.getResource()))
startTimer()
return true
}
private var timer: Timer? = null
private var timerTask: TimerTask? = null
var oldTime: Long = 0
companion object {
private val TAG = "AppC"
private var LOCATION_INTERVAL:Long = 0
private var LOCATION_DISTANCE = 10f
}
private fun startTimer(){
timer = Timer()
initializeTimerTask()
val apiService = ApiService(Handler())
App.courInfo = Prefs.instance(App.preferences).getCourierLocInfo()
notificationManager = NotificationManagerCompat.from(this);
model = MyRestModel(apiService)
model!!.apiCallback = ApiCallback()
Log.e(TAG, "onCreate")
initializeLocationManager()
setLocationUpdates()
timer!!.schedule(timerTask, NeverEndingJob.LOCATION_INTERVAL, NeverEndingJob.LOCATION_INTERVAL) //
}
internal var mLastLocation: Location =Location(LocationManager.NETWORK_PROVIDER)
fun initializeTimerTask() {
timerTask = object : TimerTask() {
override fun run() {
Handler(Looper.getMainLooper()).post {
setLocationUpdates()
}
}
}
}
private fun stopTimer(){
if (timer != null) {
timer?.cancel();
timer = null;
}
}
private fun getNotification(title : String): Notification {
Log.d(TAG, "create notofication")
val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
NotificationCompat.Builder(this, CHANNEL_ID)
else
NotificationCompat.Builder(this)
builder.setSmallIcon(R.mipmap.ic_launcher)
.setWhen(0)
builder.setContentTitle(title)
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher, null))
val not: Notification
not = builder.setOngoing(true).build()
return not
}
private inner class LocationListener(provider: String) : android.location.LocationListener {
init {
Log.e(TAG, "LocationListener $provider")
mLastLocation = Location(provider)
}
override fun onLocationChanged(location: Location) {
Log.e(TAG, "onLocationChanged: $location")
try{
val distance = location.distanceTo(mLastLocation)
mNotificationManager!!.notify(NOTIFICATION_ID, getNotification("${R.string.get_location.getResource()} Метр: ${distance.toInt()}"))
Handler().postDelayed({
mNotificationManager!!.notify(NOTIFICATION_ID, getNotification(R.string.working.getResource()))
},3000)
Log.e(TAG,"distance"+ "$distance")
if (distance > 10){
mLastLocation = location
sendLocation(mLastLocation)
}
Prefs.instance(App.preferences).setLastLocate(location)
Prefs.instance(App.preferences).setLastLocate(location)
}catch (e :java.lang.Exception){
Log.e(TAG, "send http lat lon exception: $e")
}
mLastLocation.set(location)
}
override fun onProviderDisabled(provider: String) {
Log.e(TAG, "onProviderDisabled: $provider")
}
override fun onProviderEnabled(provider: String) {
Log.e(TAG, "onProviderEnabled: $provider")
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
Log.e(TAG, "onStatusChanged: $provider")
}
}
fun sendLocation(location : Location){
Log.e(TAG, "ready for send http lat lon $location ")
val currentTime = Functions.calculateDifference(System.currentTimeMillis())
Log.e(TAG, "get current time $currentTime ")
if (App.courInfo != null){
Log.e(TAG, "get open time ${App.courInfo!!.startTime} ")
Log.e(TAG, "get close time ${App.courInfo!!.endTime} ")
val startTime = App.courInfo!!.startTime
val endTime = App.courInfo!!.endTime
val isHourLess =currentTime.hour.toInt() > startTime.hour.toInt()
val isHourLessEqual =currentTime.hour.toInt() == startTime.hour.toInt()
val isMinLess = currentTime.minute.toInt() >= startTime.minute.toInt()
val isHourMore =currentTime.hour.toInt() < endTime.hour.toInt()
val isHourMoreEqual =currentTime.hour.toInt() == endTime.hour.toInt()
val isMinMore = currentTime.minute.toInt() <= endTime.minute.toInt()
if (isHourLess && isHourMore){
if (model != null){
model!!.setLocation(App.userData!!.phone, App.userData!!.token, App.userData!!.cId,location.latitude.toString(),location.longitude.toString())
}
}else if (isHourLessEqual && isHourMore){
if (isMinLess){
if (model != null){
model!!.setLocation(App.userData!!.phone, App.userData!!.token, App.userData!!.cId,location.latitude.toString(),location.longitude.toString())
}
}
}else if (isHourLess && isHourMoreEqual){
if (isMinMore){
if (model != null){
model!!.setLocation(App.userData!!.phone, App.userData!!.token, App.userData!!.cId,location.latitude.toString(),location.longitude.toString())
}
}
}
}
}
private fun initializeLocationManager() {
Log.e(TAG, "initializeLocationManager")
mLocationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
}
fun setLocationUpdates(){
try {
val locInfo = Prefs.instance(App.preferences).getCourierLocInfo()
if (locInfo != null){
NeverEndingJob.LOCATION_INTERVAL = 3000
NeverEndingJob.LOCATION_DISTANCE = locInfo.metres.toFloat()
}
Log.e(TAG, "onCreate $locInfo")
mLocationManager!!.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1000, 0.toFloat(),
mLocationListeners[1])
} catch (ex: java.lang.SecurityException) {
Log.i(TAG, "fail to request location update, ignore", ex)
} catch (ex: IllegalArgumentException) {
Log.d(TAG, "network provider does not exist, " + ex.message)
}
}
inner class ApiCallback : ApiService.Callback{
override fun onSuccess(result: String) {
mNotificationManager!!.notify(NOTIFICATION_ID, getNotification(R.string.success_sended.getResource()))
Handler().postDelayed({
mNotificationManager!!.notify(NOTIFICATION_ID, getNotification(R.string.working.getResource()))
},3000)
notificationManager!!.notify(1,mBuilder.build())
}
override fun onFail(failure: String) {
Log.e(TAG, "onFail $failure")
}
}
fun clearLocationListeners(){
if (mLocationManager != null) {
for (i in mLocationListeners.indices) {
try {
mLocationManager!!.removeUpdates(mLocationListeners[i])
} catch (ex: Exception) {
Log.i(NeverEndingJob.TAG, "fail to remove location listners, ignore", ex)
}
}
}
}
}
This code work perfectly when app is opened, send every time location to server, but when I close the app, service works every minute says me you should get location but onLocationChanged not called:
timerTask = object : TimerTask() {
override fun run() {
Handler(Looper.getMainLooper()).post {
setLocationUpdates()
}
}
}
Manifest:
<service
android:name=".service.NeverEndingJob"
android:enabled="true">
<!--<intent-filter>-->
<!--<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>-->
<!--</intent-filter>-->
</service>
<receiver
android:name=".service.RestartService"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped">
<intent-filter>
<action android:name="app.name.RestartService" />
<action android:name="android.net.wifi.STATE_CHANGE" />
<!--<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />-->
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Receiver
class RestartService : BroadcastReceiver() {
var TAG = "RestartService"
override fun onReceive(p0: Context?, p1: Intent?) {
MLog.d(TAG,"service stopppedd")
val dispatcher = FirebaseJobDispatcher(GooglePlayDriver(p0))
val job = dispatcher
.newJobBuilder()
.setService(NeverEndingJob::class.java)
.setTag("AppC-Bg-Job")
.setRecurring(false)
.setLifetime(Lifetime.FOREVER)
.setTrigger(Trigger.executionWindow(0,0))
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(Constraint.ON_ANY_NETWORK)
.build()
dispatcher.mustSchedule(job)
}
}
Activity:
lateinit var dispatcher: FirebaseJobDispatcher
private fun startLocationService() {
dispatcher = FirebaseJobDispatcher(GooglePlayDriver(this))
val job = dispatcher
.newJobBuilder()
.setService(NeverEndingJob::class.java)
.setTag("Bringo-Couirer-Bg-Job")
.setRecurring(false)
.setLifetime(Lifetime.FOREVER)
.setTrigger(Trigger.executionWindow(0, 0))
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(Constraint.ON_ANY_NETWORK)
.build()
dispatcher.mustSchedule(job)
}
override fun onDestroy() {
super.onDestroy()
dispatcher.cancelAll()
}
Why onLocationChanged not called after few minutes when app is closed in alive service?
I think you need to use the Location Manager.
LocationManager locationManager = (LocationManager)
getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER,
2000,
10, this);
the first parameters is the provider you are using (in this case the GPS Provider). The second parameter (2000) is the minimum time in milliseconds between each update. The third parameter (10) is the minimum distance. The last parameters is your LocationListener (this).
It is also a good idea to implement onProviderDisabled in case the user has his GPS turned off.
override this method in ur service
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Utils.showLog("onTaskedremoved called");
PendingIntent service = PendingIntent.getService(
getApplicationContext(),
1001,
new Intent(getApplicationContext(), ''your service name''.class),
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
}