From 7efe05011fb43839e1cb3e5d8307bb0faf398631 Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Sun, 3 Nov 2024 19:33:21 +0100 Subject: [PATCH] action: torch --- .../de/jrpie/android/launcher/Application.kt | 10 +- .../launcher/actions/LauncherAction.kt | 26 +++++- .../android/launcher/actions/TorchManager.kt | 91 +++++++++++++++++++ .../drawable/baseline_flashlight_on_24.xml | 16 ++++ app/src/main/res/values/strings.xml | 3 + 5 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/de/jrpie/android/launcher/actions/TorchManager.kt create mode 100644 app/src/main/res/drawable/baseline_flashlight_on_24.xml diff --git a/app/src/main/java/de/jrpie/android/launcher/Application.kt b/app/src/main/java/de/jrpie/android/launcher/Application.kt index 88a0fb3..0a1c0e8 100644 --- a/app/src/main/java/de/jrpie/android/launcher/Application.kt +++ b/app/src/main/java/de/jrpie/android/launcher/Application.kt @@ -1,14 +1,22 @@ package de.jrpie.android.launcher +import android.os.Build +import android.os.Build.VERSION_CODES import androidx.preference.PreferenceManager +import de.jrpie.android.launcher.actions.TorchManager import de.jrpie.android.launcher.preferences.LauncherPreferences class Application : android.app.Application() { + var torchManager: TorchManager? = null override fun onCreate() { super.onCreate() + if (Build.VERSION.SDK_INT >= VERSION_CODES.M) { + torchManager = TorchManager(this) + } + val preferences = PreferenceManager.getDefaultSharedPreferences(this) LauncherPreferences.init(preferences, this.resources) } -} \ No newline at end of file +} diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt index 562ce44..05f8a13 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt @@ -5,10 +5,15 @@ import android.content.Intent import android.content.SharedPreferences.Editor import android.graphics.Rect import android.graphics.drawable.Drawable +import android.hardware.camera2.CameraAccessException +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager import android.media.AudioManager +import android.os.Build import android.os.SystemClock import android.view.KeyEvent import android.widget.Toast +import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.R import de.jrpie.android.launcher.apps.AppFilter import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER @@ -37,7 +42,7 @@ enum class LauncherAction( "launcher:chooseFromFavorites", R.string.list_other_list_favorites, R.drawable.baseline_favorite_24, - { context -> openAppsList(context, true)} + { context -> openAppsList(context, true) } ), VOLUME_UP( "launcher:volumeUp", @@ -77,6 +82,12 @@ enum class LauncherAction( R.drawable.baseline_lock_24px, LauncherDeviceAdmin::lockScreen ), + TORCH( + "launcher:toggleTorch", + R.string.list_other_torch, + R.drawable.baseline_flashlight_on_24, + ::toggleTorch + ), NOP("launcher:nop", R.string.list_other_nop, R.drawable.baseline_not_interested_24, {}); override fun invoke(context: Context, rect: Rect?): Boolean { @@ -171,6 +182,19 @@ private fun audioVolumeDown(context: Context) { } /* End media player actions */ +private fun toggleTorch(context: Context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + Toast.makeText( + context, + context.getString(R.string.alert_requires_android_m), + Toast.LENGTH_LONG + ).show() + return + } + + (context.applicationContext as Application).torchManager?.toggleTorch(context) +} + private fun expandNotificationsPanel(context: Context) { /* https://stackoverflow.com/a/15582509 */ try { diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/TorchManager.kt b/app/src/main/java/de/jrpie/android/launcher/actions/TorchManager.kt new file mode 100644 index 0000000..166373c --- /dev/null +++ b/app/src/main/java/de/jrpie/android/launcher/actions/TorchManager.kt @@ -0,0 +1,91 @@ +package de.jrpie.android.launcher.actions + +import android.annotation.SuppressLint +import android.content.Context +import android.hardware.camera2.CameraAccessException +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager +import android.os.Build +import android.os.Build.VERSION_CODES +import android.os.Handler +import android.os.Looper +import android.widget.Toast +import androidx.annotation.RequiresApi +import de.jrpie.android.launcher.R + +@RequiresApi(VERSION_CODES.M) +class TorchManager(context: Context) { + + private val camera = getCameraId(context) + private var torchEnabled = false + + private val torchCallback = object : CameraManager.TorchCallback() { + override fun onTorchModeChanged(cameraId: String, enabled: Boolean) { + synchronized(this@TorchManager) { + if (cameraId == camera) { + torchEnabled = enabled + } + } + } + } + + init { + registerCallback(context) + } + + private fun getCameraId(context: Context): String? { + val cameraManager = + context.getSystemService(Context.CAMERA_SERVICE) as CameraManager + + return cameraManager.cameraIdList.firstOrNull { c -> + cameraManager + .getCameraCharacteristics(c) + .get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false + } + } + + private fun registerCallback(context: Context) { + val cameraManager = + context.getSystemService(Context.CAMERA_SERVICE) as CameraManager + + cameraManager.registerTorchCallback( + torchCallback, + Handler(Looper.getMainLooper()) + ) + } + + fun toggleTorch(context: Context) { + synchronized(this) { + val cameraManager = + context.getSystemService(Context.CAMERA_SERVICE) as CameraManager + + if (camera == null) { + Toast.makeText( + context, + context.getString(R.string.alert_no_torch_found), + Toast.LENGTH_LONG + ).show() + return + } + + try { + if (!torchEnabled && Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + cameraManager.turnOnTorchWithStrengthLevel( + camera, + cameraManager.getCameraCharacteristics(camera) + .get(CameraCharacteristics.FLASH_INFO_STRENGTH_MAXIMUM_LEVEL) ?: 1 + ) + } else { + cameraManager.setTorchMode(camera, !torchEnabled) + } + + } catch (e: CameraAccessException) { + Toast.makeText( + context, + context.getString(R.string.alert_torch_access_exception), + Toast.LENGTH_LONG + ).show() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_flashlight_on_24.xml b/app/src/main/res/drawable/baseline_flashlight_on_24.xml new file mode 100644 index 0000000..e1326ae --- /dev/null +++ b/app/src/main/res/drawable/baseline_flashlight_on_24.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 17bdff8..637dfd3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -234,10 +234,13 @@ Settings More options Error: Can\'t expand status bar.\nThis action is using functionality that is not part of the published Android API. Unfortunately, it does not seem to work on your device. + This functionality requires Android 6.0 or later. App hidden. You can make it visible again in settings. Undo Quick Settings µLauncher needs to be a device admin in order to lock the screen. This is required for the lock screen action. Enable the lock screen action + No camera with torch detected. + Error: Can\'t access torch.