mirror of
https://github.com/SimpleMobileTools/Simple-Flashlight.git
synced 2025-02-01 19:06:46 +01:00
cleanup flash implementation
- fix issues with brightness control - add separate classes for Android 5 and Android 6+ for simplicity - properly cleanup camera in onDestory and also when stroboscope/flashlight mode are disabled
This commit is contained in:
parent
87b678e4bb
commit
98c0f0578e
@ -9,7 +9,6 @@ import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageView
|
||||
import androidx.core.view.isVisible
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.LICENSE_EVENT_BUS
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_CAMERA
|
||||
@ -183,7 +182,7 @@ class MainActivity : SimpleActivity() {
|
||||
private fun setupCameraImpl() {
|
||||
mCameraImpl = MyCameraImpl.newInstance(this, object : CameraTorchListener {
|
||||
override fun onTorchEnabled(isEnabled: Boolean) {
|
||||
if (mCameraImpl?.supportsBrightnessControl() == true) {
|
||||
if (mCameraImpl!!.supportsBrightnessControl()) {
|
||||
brightness_bar.beVisibleIf(isEnabled)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.simplemobiletools.flashlight.helpers
|
||||
|
||||
interface CameraFlash {
|
||||
fun initialize()
|
||||
fun toggleFlashlight(enable: Boolean)
|
||||
fun changeTorchBrightness(level: Int) {}
|
||||
fun getMaximumBrightnessLevel(): Int = DEFAULT_BRIGHTNESS_LEVEL
|
||||
fun supportsBrightnessControl(): Boolean = false
|
||||
fun getCurrentBrightnessLevel(): Int = DEFAULT_BRIGHTNESS_LEVEL
|
||||
fun unregisterListeners(){}
|
||||
fun release(){}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.simplemobiletools.flashlight.helpers
|
||||
|
||||
import android.graphics.SurfaceTexture
|
||||
import android.hardware.Camera
|
||||
|
||||
class LollipopCameraFlash : CameraFlash {
|
||||
private var camera: Camera? = null
|
||||
private var params: Camera.Parameters? = null
|
||||
|
||||
override fun toggleFlashlight(enable: Boolean) {
|
||||
if (camera == null || params == null || camera!!.parameters == null) {
|
||||
return
|
||||
}
|
||||
val flashMode = if (enable) Camera.Parameters.FLASH_MODE_ON else Camera.Parameters.FLASH_MODE_OFF
|
||||
params!!.flashMode = flashMode
|
||||
camera!!.parameters = params
|
||||
if (enable) {
|
||||
val dummy = SurfaceTexture(1)
|
||||
camera!!.setPreviewTexture(dummy)
|
||||
camera!!.startPreview()
|
||||
}
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
camera = Camera.open()
|
||||
params = camera!!.parameters
|
||||
params!!.flashMode = Camera.Parameters.FLASH_MODE_OFF
|
||||
camera!!.parameters = params
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
camera!!.release()
|
||||
camera = null
|
||||
}
|
||||
}
|
@ -13,10 +13,10 @@ import com.simplemobiletools.flashlight.models.Events
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
internal class PostMarshmallowCamera(
|
||||
internal class MarshmallowPlusCameraFlash(
|
||||
private val context: Context,
|
||||
private val cameraTorchListener: CameraTorchListener? = null,
|
||||
) {
|
||||
private var cameraTorchListener: CameraTorchListener? = null,
|
||||
) : CameraFlash {
|
||||
|
||||
private val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
||||
private var cameraId: String? = null
|
||||
@ -35,12 +35,13 @@ internal class PostMarshmallowCamera(
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleMarshmallowFlashlight(enable: Boolean) {
|
||||
override fun toggleFlashlight(enable: Boolean) {
|
||||
try {
|
||||
manager.setTorchMode(cameraId!!, enable)
|
||||
if (enable) {
|
||||
if (supportsBrightnessControl() && enable) {
|
||||
val brightnessLevel = getCurrentBrightnessLevel()
|
||||
changeTorchBrightness(brightnessLevel)
|
||||
} else {
|
||||
manager.setTorchMode(cameraId!!, enable)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
@ -51,13 +52,13 @@ internal class PostMarshmallowCamera(
|
||||
}
|
||||
}
|
||||
|
||||
fun changeTorchBrightness(level: Int) {
|
||||
override fun changeTorchBrightness(level: Int) {
|
||||
if (isTiramisuPlus()) {
|
||||
manager.turnOnTorchWithStrengthLevel(cameraId!!, level)
|
||||
}
|
||||
}
|
||||
|
||||
fun getMaximumBrightnessLevel(): Int {
|
||||
override fun getMaximumBrightnessLevel(): Int {
|
||||
return if (isTiramisuPlus()) {
|
||||
val characteristics = manager.getCameraCharacteristics(cameraId!!)
|
||||
characteristics.get(CameraCharacteristics.FLASH_INFO_STRENGTH_MAXIMUM_LEVEL) ?: MIN_BRIGHTNESS_LEVEL
|
||||
@ -66,12 +67,12 @@ internal class PostMarshmallowCamera(
|
||||
}
|
||||
}
|
||||
|
||||
fun supportsBrightnessControl(): Boolean {
|
||||
override fun supportsBrightnessControl(): Boolean {
|
||||
val maxBrightnessLevel = getMaximumBrightnessLevel()
|
||||
return maxBrightnessLevel > MIN_BRIGHTNESS_LEVEL
|
||||
}
|
||||
|
||||
fun getCurrentBrightnessLevel(): Int {
|
||||
override fun getCurrentBrightnessLevel(): Int {
|
||||
var brightnessLevel = context.config.brightnessLevel
|
||||
if (brightnessLevel == DEFAULT_BRIGHTNESS_LEVEL) {
|
||||
brightnessLevel = getMaximumBrightnessLevel()
|
||||
@ -79,11 +80,15 @@ internal class PostMarshmallowCamera(
|
||||
return brightnessLevel
|
||||
}
|
||||
|
||||
fun initialize() {
|
||||
override fun initialize() {
|
||||
manager.registerTorchCallback(torchCallback, Handler(context.mainLooper))
|
||||
}
|
||||
|
||||
fun cleanUp() {
|
||||
override fun unregisterListeners() {
|
||||
manager.unregisterTorchCallback(torchCallback)
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
cameraTorchListener = null
|
||||
}
|
||||
}
|
@ -1,36 +1,29 @@
|
||||
package com.simplemobiletools.flashlight.helpers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.SurfaceTexture
|
||||
import android.hardware.Camera
|
||||
import android.os.Handler
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.commons.helpers.isMarshmallowPlus
|
||||
import com.simplemobiletools.commons.helpers.isNougatPlus
|
||||
import com.simplemobiletools.flashlight.R
|
||||
import com.simplemobiletools.flashlight.extensions.config
|
||||
import com.simplemobiletools.flashlight.extensions.updateWidgets
|
||||
import com.simplemobiletools.flashlight.models.Events
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class MyCameraImpl private constructor(val context: Context, private val cameraTorchListener: CameraTorchListener?) {
|
||||
class MyCameraImpl private constructor(val context: Context, private var cameraTorchListener: CameraTorchListener? = null) {
|
||||
var stroboFrequency = 1000L
|
||||
|
||||
companion object {
|
||||
var isFlashlightOn = false
|
||||
private val SOS = arrayListOf(250L, 250L, 250L, 250L, 250L, 250L, 500L, 250L, 500L, 250L, 500L, 250L, 250L, 250L, 250L, 250L, 250L, 1000L)
|
||||
|
||||
private var camera: Camera? = null
|
||||
private var params: Camera.Parameters? = null
|
||||
private var isMarshmallow = false
|
||||
private var shouldEnableFlashlight = false
|
||||
private var shouldEnableStroboscope = false
|
||||
private var shouldEnableSOS = false
|
||||
private var isStroboSOS = false // are we sending SOS, or casual stroboscope?
|
||||
|
||||
private var marshmallowCamera: PostMarshmallowCamera? = null
|
||||
private var cameraFlash: CameraFlash? = null
|
||||
|
||||
@Volatile
|
||||
private var shouldStroboscopeStop = false
|
||||
@ -45,7 +38,6 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
}
|
||||
|
||||
init {
|
||||
isMarshmallow = isMarshmallowPlus()
|
||||
handleCameraSetup()
|
||||
stroboFrequency = context.config.stroboscopeFrequency
|
||||
}
|
||||
@ -56,6 +48,9 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
}
|
||||
|
||||
fun toggleStroboscope(): Boolean {
|
||||
handleCameraSetup()
|
||||
cameraFlash!!.unregisterListeners()
|
||||
|
||||
if (isSOSRunning) {
|
||||
stopSOS()
|
||||
shouldEnableStroboscope = true
|
||||
@ -86,6 +81,9 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
}
|
||||
|
||||
fun toggleSOS(): Boolean {
|
||||
handleCameraSetup()
|
||||
cameraFlash!!.unregisterListeners()
|
||||
|
||||
if (isStroboscopeRunning) {
|
||||
stopStroboscope()
|
||||
shouldEnableSOS = true
|
||||
@ -120,59 +118,26 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
}
|
||||
|
||||
private fun tryInitCamera(): Boolean {
|
||||
if (!isNougatPlus()) {
|
||||
if (camera == null) {
|
||||
initCamera()
|
||||
}
|
||||
|
||||
if (camera == null) {
|
||||
context.toast(R.string.camera_error)
|
||||
return false
|
||||
}
|
||||
handleCameraSetup()
|
||||
if (cameraFlash == null) {
|
||||
context.toast(R.string.camera_error)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun handleCameraSetup() {
|
||||
if (isMarshmallow) {
|
||||
setupMarshmallowCamera()
|
||||
} else {
|
||||
setupCamera()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun setupMarshmallowCamera() {
|
||||
if (marshmallowCamera == null) {
|
||||
marshmallowCamera = PostMarshmallowCamera(context, cameraTorchListener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCamera() {
|
||||
if (isMarshmallow) {
|
||||
return
|
||||
}
|
||||
|
||||
if (camera == null) {
|
||||
initCamera()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCamera() {
|
||||
try {
|
||||
camera = Camera.open()
|
||||
params = camera!!.parameters
|
||||
params!!.flashMode = Camera.Parameters.FLASH_MODE_OFF
|
||||
camera!!.parameters = params
|
||||
if (cameraFlash == null) {
|
||||
cameraFlash = if (isMarshmallowPlus()) MarshmallowPlusCameraFlash(context, cameraTorchListener) else LollipopCameraFlash()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
EventBus.getDefault().post(Events.CameraUnavailable())
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFlashlight() {
|
||||
if (camera == null) {
|
||||
handleCameraSetup()
|
||||
}
|
||||
handleCameraSetup()
|
||||
|
||||
if (isFlashlightOn) {
|
||||
enableFlashlight()
|
||||
@ -188,30 +153,16 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
return
|
||||
}
|
||||
|
||||
if (isMarshmallow) {
|
||||
toggleMarshmallowFlashlight(true)
|
||||
} else {
|
||||
try {
|
||||
if (camera == null || params == null || camera!!.parameters == null) {
|
||||
return
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
|
||||
params!!.flashMode = Camera.Parameters.FLASH_MODE_TORCH
|
||||
camera!!.parameters = params
|
||||
try {
|
||||
camera!!.startPreview()
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
disableFlashlight()
|
||||
}
|
||||
try {
|
||||
cameraFlash!!.initialize()
|
||||
cameraFlash!!.toggleFlashlight(true)
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
disableFlashlight()
|
||||
}
|
||||
|
||||
val mainRunnable = Runnable { stateChanged(true) }
|
||||
Handler(context.mainLooper).post(mainRunnable)
|
||||
marshmallowCamera?.initialize()
|
||||
}
|
||||
|
||||
private fun disableFlashlight() {
|
||||
@ -219,22 +170,13 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
return
|
||||
}
|
||||
|
||||
if (isMarshmallow) {
|
||||
toggleMarshmallowFlashlight(false)
|
||||
} else {
|
||||
try {
|
||||
if (camera == null || params == null || camera!!.parameters == null) {
|
||||
return
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
|
||||
params!!.flashMode = Camera.Parameters.FLASH_MODE_OFF
|
||||
camera!!.parameters = params
|
||||
try {
|
||||
cameraFlash!!.toggleFlashlight(false)
|
||||
} catch (e: Exception) {
|
||||
context.showErrorToast(e)
|
||||
disableFlashlight()
|
||||
}
|
||||
stateChanged(false)
|
||||
releaseCamera()
|
||||
}
|
||||
|
||||
private fun stateChanged(isEnabled: Boolean) {
|
||||
@ -243,21 +185,19 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
context.updateWidgets(isEnabled)
|
||||
}
|
||||
|
||||
private fun toggleMarshmallowFlashlight(enable: Boolean) {
|
||||
marshmallowCamera!!.toggleMarshmallowFlashlight(enable)
|
||||
}
|
||||
|
||||
fun releaseCamera() {
|
||||
cameraFlash?.unregisterListeners()
|
||||
|
||||
if (isFlashlightOn) {
|
||||
disableFlashlight()
|
||||
}
|
||||
|
||||
camera?.release()
|
||||
camera = null
|
||||
cameraFlash?.release()
|
||||
cameraFlash = null
|
||||
cameraTorchListener = null
|
||||
|
||||
isFlashlightOn = false
|
||||
shouldStroboscopeStop = true
|
||||
marshmallowCamera?.cleanUp()
|
||||
}
|
||||
|
||||
private val stroboscope = Runnable {
|
||||
@ -273,56 +213,28 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
}
|
||||
|
||||
var sosIndex = 0
|
||||
if (isNougatPlus()) {
|
||||
while (!shouldStroboscopeStop) {
|
||||
try {
|
||||
marshmallowCamera!!.toggleMarshmallowFlashlight(true)
|
||||
val onDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
|
||||
Thread.sleep(onDuration)
|
||||
marshmallowCamera!!.toggleMarshmallowFlashlight(false)
|
||||
val offDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
|
||||
Thread.sleep(offDuration)
|
||||
} catch (e: Exception) {
|
||||
shouldStroboscopeStop = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (camera == null) {
|
||||
initCamera()
|
||||
}
|
||||
|
||||
handleCameraSetup()
|
||||
while (!shouldStroboscopeStop) {
|
||||
try {
|
||||
val torchOn = camera!!.parameters ?: return@Runnable
|
||||
val torchOff = camera!!.parameters
|
||||
torchOn.flashMode = Camera.Parameters.FLASH_MODE_TORCH
|
||||
torchOff.flashMode = Camera.Parameters.FLASH_MODE_OFF
|
||||
|
||||
val dummy = SurfaceTexture(1)
|
||||
camera!!.setPreviewTexture(dummy)
|
||||
|
||||
camera!!.startPreview()
|
||||
|
||||
while (!shouldStroboscopeStop) {
|
||||
camera!!.parameters = torchOn
|
||||
val onDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
|
||||
Thread.sleep(onDuration)
|
||||
camera!!.parameters = torchOff
|
||||
val offDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
|
||||
Thread.sleep(offDuration)
|
||||
}
|
||||
|
||||
if (camera != null) {
|
||||
camera!!.parameters = torchOff
|
||||
if (!shouldEnableFlashlight || isMarshmallow) {
|
||||
camera!!.release()
|
||||
camera = null
|
||||
}
|
||||
}
|
||||
} catch (e: RuntimeException) {
|
||||
cameraFlash!!.toggleFlashlight(true)
|
||||
val onDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
|
||||
Thread.sleep(onDuration)
|
||||
cameraFlash!!.toggleFlashlight(false)
|
||||
val offDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
|
||||
Thread.sleep(offDuration)
|
||||
} catch (e: Exception) {
|
||||
shouldStroboscopeStop = true
|
||||
}
|
||||
}
|
||||
|
||||
// disable flash immediately if stroboscope is stopped and normal flash mode is disabled
|
||||
if (shouldStroboscopeStop && !shouldEnableFlashlight) {
|
||||
handleCameraSetup()
|
||||
cameraFlash!!.toggleFlashlight(false)
|
||||
cameraFlash!!.release()
|
||||
cameraFlash = null
|
||||
}
|
||||
|
||||
shouldStroboscopeStop = false
|
||||
if (isStroboSOS) {
|
||||
isSOSRunning = false
|
||||
@ -347,26 +259,18 @@ class MyCameraImpl private constructor(val context: Context, private val cameraT
|
||||
}
|
||||
|
||||
fun getMaximumBrightnessLevel(): Int {
|
||||
return if (isMarshmallow) {
|
||||
marshmallowCamera?.getMaximumBrightnessLevel() ?: MIN_BRIGHTNESS_LEVEL
|
||||
} else MIN_BRIGHTNESS_LEVEL
|
||||
return cameraFlash!!.getMaximumBrightnessLevel()
|
||||
}
|
||||
|
||||
fun getCurrentBrightnessLevel(): Int {
|
||||
return if (isMarshmallow) {
|
||||
marshmallowCamera?.getCurrentBrightnessLevel() ?: MIN_BRIGHTNESS_LEVEL
|
||||
} else MIN_BRIGHTNESS_LEVEL
|
||||
return cameraFlash!!.getCurrentBrightnessLevel()
|
||||
}
|
||||
|
||||
fun supportsBrightnessControl(): Boolean {
|
||||
return if (isMarshmallow) {
|
||||
marshmallowCamera?.supportsBrightnessControl() ?: false
|
||||
} else false
|
||||
return cameraFlash!!.supportsBrightnessControl()
|
||||
}
|
||||
|
||||
fun updateBrightnessLevel(level: Int) {
|
||||
if (isMarshmallow) {
|
||||
marshmallowCamera?.changeTorchBrightness(level)
|
||||
}
|
||||
cameraFlash!!.changeTorchBrightness(level)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user