0

at this part lines 49-51

override fun onBackPressed() {

    }

I tried to look in google but there are so many answers and so different.

now when I'm running the application from the phone I need then to make a long press on the application icon on the phone then info>force stop the back button does nothing the application is keep running. and I want when pressing once the back button that it will close and exit the application.

full code

package com.example.flashlight

import android.content.Context
import android.content.Intent
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.SeekBar
import android.widget.TextView
import kotlin.math.log

class MainActivity : AppCompatActivity() {

    var flashLightStatus: Boolean = false
    var flashLightOn = false
    var counter: Long = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView = findViewById<TextView>(R.id.textView)
        val seek = findViewById<SeekBar>(R.id.seekBar)
        seek.setOnSeekBarChangeListener(
            object : SeekBar.OnSeekBarChangeListener {
                override fun onProgressChanged(
                    seekBar: SeekBar?,
                    progress: Int,
                    fromUser: Boolean
                ) {
                    // Log.d("seekbar", "Your Progress: ${seekBar?.progress}"

                    counter = progress.toLong()
                    textView.text = counter.toString()
                }

                override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
                override fun onStopTrackingTouch(seekBar: SeekBar?) = Unit
            },
        )

        startFlashLight()

    }


    override fun onBackPressed() {
        
    }

    private fun openFlashLight() {
        val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
        val cameraId = cameraManager.cameraIdList[0]
        if (!flashLightStatus) {
            try {
                cameraManager.setTorchMode(cameraId, true)
                flashLightStatus = true

            } catch (e: CameraAccessException) {
            }
        } else {
            try {
                cameraManager.setTorchMode(cameraId, false)
                flashLightStatus = false
            } catch (e: CameraAccessException) {
            }
        }
    }

    private fun closeFlashLight()
    {
        val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
        val cameraId = cameraManager.cameraIdList[0]
        if (flashLightStatus) {
            try {
                cameraManager.setTorchMode(cameraId, false)
                flashLightStatus = false

            } catch (e: CameraAccessException) {
            }
        } else {
            try {
                cameraManager.setTorchMode(cameraId, true)
                flashLightStatus = true
            } catch (e: CameraAccessException) {
            }
        }
    }

    fun startFlashLight() {
        openFlashLight()
        flashLightOn = true

        val handler = Handler(Looper.getMainLooper())
        handler.postDelayed(object: Runnable {
            override fun run() {
                if (flashLightOn) {
                    closeFlashLight()
                } else {
                    openFlashLight()
                }

                // Changing the toggle and calling the same Runnable again after 5 seconds
                flashLightOn = !flashLightOn
                handler.postDelayed(this, counter)
            }
        }, counter)
    }
}
Daniel Lip
  • 3,867
  • 7
  • 58
  • 120
  • The whole application (which is highly discouraged) or just the task? Call `finishAndRemoveTask()` – Tenfour04 May 23 '23 at 04:18
  • @Tenfour04 tried it now and it's closing visual the application, but the phone flashlight is still open and working. in my application the flashlight is blinking at some speed and when I put the line and press the back button on the phone it's closing the app but only make it back to the phone main window the app itself is still working. – Daniel Lip May 23 '23 at 04:23
  • @Tenfour04 so far what is working for me is this line: android.os.Process.killProcess(android.os.Process.myPid()) I'm not sure if it's ok to do it this way but this is the only thing that work. and I tried any solution in google I found. is there any other way I should prefer and use then this? – Daniel Lip May 23 '23 at 06:48
  • Definitely you should not do this by killing your app process. Fix the critical bug instead of dumping your app from memory as a hack. Killing the app process subverts the way Android is designed to manage memory. It won’t be able to quickly resume your app when the user wants to reopen it. – Tenfour04 May 23 '23 at 10:57
  • 1
    It’s because the way you created your loop with the handlers leaks the activity and the jobs you endlessly send it. If you want to do this with Handler, you have to store it in a property instead of local variable inside your function, and clear all of its messages in onDestroy. It would be easier to do your loop using a coroutine on `lifecycleScope` because then it will clean itself up automatically as long as you make sure your coroutine cooperates with cancellation. – Tenfour04 May 23 '23 at 11:12
  • @Tenfour04 I will have to dig about and see if I can do it. I will report if/when success. Thanks a lot. – Daniel Lip May 23 '23 at 12:32
  • 1
    Just move this: `val handler = Handler(Looper.getMainLooper())` into a class level property. And add `override fun onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null) }`. Also add `handler.removeCallbacksAndMessages(null)` as the first line in `startFlashlight()` to avoid the possibility of starting more chain loop at a time. – Tenfour04 May 23 '23 at 12:34
  • @Tenfour04 how do I make the class level property? I tried this before the main activity: public class LooperHanlder { public val handler = Handler(Looper.getMainLooper()) } – Daniel Lip May 23 '23 at 15:33
  • 1
    Keep the line exactly as you had it before, but move it right under the `var counter` line. Sorry if "class level property" was confusing. I just mean make it a property of your MainActivity class instead of a local variable. – Tenfour04 May 23 '23 at 15:39
  • @Tenfour04 I'm getting error in your answer on the line: handler.postDelayed(this, counter) on the 'this' Type mismatch Require Runnable – Daniel Lip May 23 '23 at 17:51
  • @Tenfour04 getting error on the this@Runnable line: handler.postDelayed(this@Runnable, counter) 'this' is not defined in this context. from what I read now in google , this is only available after the super class has been initialized. maybe it's because I'm using kotlin? – Daniel Lip May 23 '23 at 19:56
  • @Tenfour04 not working. still when pressing the back button on the phone the app close but the flashlight keep working. this is my complete code after the changes: https://pastebin.com/U7rWwrwY – Daniel Lip May 23 '23 at 20:09
  • @Tenfour04 it's working if I move the code from onDestroy to onBackPressed. does it fine? override fun onBackPressed() { handler.removeCallbacksAndMessages(null) super.onBackPressed() } – Daniel Lip May 23 '23 at 22:34
  • Last thing I tried is to use finish() and System.exit(0) both in the onBakcPressed method. now it's shutting down the application but when I'm looking in the hone on the flshlight the image of it blinking for more 2-10 seconds before stop. the flashlight turned off but the flashlight logo/image is keep turning on/off very fast and after some seconds turn off. this is what I mean the flashlight log/image: the on on the right: https://imgur.com/a/o9pVzhN and in this link is my whole project: https://github.com/chocolade1972/Android-Blinking-Flashlight – user1196715 May 27 '23 at 17:36

1 Answers1

2

Since this is an XY-problem, I will answer the root problem.

You're trying to fix your bug by dumping the whole app from memory. Really, you need to fix your bug. (Aside from that, terminating the app is generally not a good idea. Read more here.)

Your bug is that you are creating an endless loop that keeps going even after the Activity is destroyed. It will be especially bad if you rotate your screen a few times and have multiple tasks going. It will be leaking many copies of your Activity and they'll all be running the same loop forever, with offset timings.

As with any resource/listener/task you create in onCreate() and need to be stopped when an Activity is destroyed, you must release or end it in onDestroy(). So in this case, move your handler to a property and release its work in onDestroy().

class MainActivity : AppCompatActivity() {

    var flashLightStatus: Boolean = false
    var flashLightOn = false
    var counter: Long = 0
    private val handler = Handler(Looper.getMainLooper())

    // ...

    fun startFlashLight() {
        openFlashLight()
        flashLightOn = true

        // DELETED HANDLER DECLARATION LINE HERE

        handler.removeCallbacksAndMessages(null) // Added this line to ensure no duplciate jobs

        handler.postDelayed(
        //... your original code
    }

    override fun onDestroy() { 
        super.onDestroy()
        handler.removeCallbacksAndMessages(null) 
    }
}

If you want to stop this flashlight flashing if someone switches to another app without closing your activity, then you should turn it off and remove the callbacks in onStop() and move the call to start them from onCreate() to onStart(). Keep the onDestroy() code to ensure cleanup.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • I'm getting error on the line: handler.postDelayed(this, counter) on the 'this' Type mismatch Require Runnable – Daniel Lip May 23 '23 at 17:52
  • Sorry, use `this@Runnable`. – Tenfour04 May 23 '23 at 19:01
  • getting error on the this@Runnable line: handler.postDelayed(this@Runnable, counter) 'this' is not defined in this context. from what I read now in google , this is only available after the super class has been initialized. maybe it's because I'm using kotlin? – Daniel Lip May 23 '23 at 19:56
  • 1
    Oh, that's right, I'm so sorry. You can't use `this` for a SAM-converting psuedo-constructor. Go back to your original syntax for that part. – Tenfour04 May 23 '23 at 19:59
  • not working. still when pressing the back button on the phone the app close but the flashlight keep working. this is my complete code after the changes: https://pastebin.com/U7rWwrwY – Daniel Lip May 23 '23 at 20:09
  • it's working if I move the code from onDestroy to onBackPressed. does it fine? override fun onBackPressed() { handler.removeCallbacksAndMessages(null) super.onBackPressed() } – Daniel Lip May 23 '23 at 22:34
  • 1
    Ooooh, I know what’s wrong. In a recent version of Android they changed the default behavior of the back button for the root activity of a task so it no longer finishes the activity. I had forgotten about that change. So you could just call `finish()` or `finishAndRemoveTask()` in `onBackPressed()`. Of course, if you want the behavior I described in the last paragraph, then you can manage it that way. – Tenfour04 May 23 '23 at 23:29
  • it's working but after the app is closed when I use the back button then when I click the app logo information, I can see the Force stop button enabled. what is that mean? that app is still alive? because it is closing the app and stop the flashlight when pressing the back button. the code as it is now: override fun onBackPressed() { finishAndRemoveTask() handler.removeCallbacksAndMessages(null) } – Daniel Lip May 24 '23 at 02:15
  • Not sure what you mean by force stop button. D you mean the “application not responding” dialog? If so, you are blocking the main thread somewhere. – Tenfour04 May 24 '23 at 03:50
  • no I mean that on the phone when you press down on the application logo you have some options like Select Add to Home Uninstall and on the top left small 'i' for info and when you press on the small 'i' in the small circle you get to a screen App info and there on the bottom right you have option to click on Force stop. and this option is enabled when I'm closing the application. and anyway when I test the application open/close many time some times the flash light stay open not blinking but stay open. and that's why I need to go to the Force stop if not the flash light will stay open. – Daniel Lip May 24 '23 at 05:12
  • This is the code in the MainActivity.kt the program is still not working goo because sometimes the flash light stay open after closing the app. I can also upload the whole project to github. https://pastebin.com/QFTpWVCH – Daniel Lip May 24 '23 at 05:14
  • 1
    I don't have time to debug everything. You probably should turn the flashlight off **and** cancel the handler messages in `onStop()` or `onDestroy()` (depending on whether you want it to keep going if someone presses the home button), because if you simply stop the loop while the light is on, it will stay on. – Tenfour04 May 24 '23 at 12:46