diff --git a/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt b/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt index 3647b6c6b..358954038 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/handlers/FlashlightHandler.kt @@ -26,6 +26,13 @@ import com.sameerasw.essentials.utils.performHapticFeedback import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withTimeoutOrNull +import kotlin.coroutines.resume class FlashlightHandler( private val service: AccessibilityService, @@ -328,24 +335,35 @@ class FlashlightHandler( return null } - fun pulseFlashlightForNotification() { - if (isTorchOn) return + private suspend fun getProximityStatus(context: Context): Boolean { + val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager ?: return false + val proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) ?: return false + + return withTimeoutOrNull(250L) { + suspendCancellableCoroutine { continuation -> + val listener = object : SensorEventListener { + override fun onSensorChanged(event: SensorEvent?) { + if (event?.sensor?.type == Sensor.TYPE_PROXIMITY) { + val distance = event.values[0] + val maxRange = event.sensor.maximumRange + val isBlocked = distance < maxRange && distance < 5f + sensorManager.unregisterListener(this) + if (continuation.isActive) { + continuation.resume(isBlocked) + } + } + } - val prefs = service.getSharedPreferences("essentials_prefs", Context.MODE_PRIVATE) - val pulseEnabled = prefs.getBoolean("flashlight_pulse_enabled", false) - if (!pulseEnabled) return - - // Face down check usually happens outside, but if we need it here we need proximity state. - // For separation of concerns, Proximity check should happen in Service or passed in. - // But current implementation checks isProximityBlocked inside pulseFlashlightForNotification. - // Refactoring opportunity: make this method blocking status agnostic or pass it in. - // I will assume for now we can skip the facedown check OR I should add a way to set proximity status. - // Let's pass 'isProximityBlocked' to this method or main service sets it. - // For cleaner API, I'll add a property `isProximityBlocked` to the handler or pass it to this method. - // Let's add a public var isProximityBlocked to the handler. - } + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} + } - var isProximityBlocked = false + sensorManager.registerListener(listener, proximitySensor, SensorManager.SENSOR_DELAY_FASTEST) + continuation.invokeOnCancellation { + sensorManager.unregisterListener(listener) + } + } + } ?: false + } fun pulseFlashlightForNotificationWithCheck(ignoreChecks: Boolean = false) { if (isTorchOn) return @@ -354,19 +372,23 @@ class FlashlightHandler( if (!ignoreChecks) { val pulseEnabled = prefs.getBoolean("flashlight_pulse_enabled", false) if (!pulseEnabled) return - - val faceDownOnly = prefs.getBoolean("flashlight_pulse_facedown_only", true) - if (faceDownOnly && !isProximityBlocked) return } val cameraId = getCameraId() ?: return - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && - FlashlightUtil.isIntensitySupported(service, cameraId) - ) { + flashlightJob?.cancel() + flashlightJob = scope.launch { + if (!ignoreChecks) { + val faceDownOnly = prefs.getBoolean("flashlight_pulse_facedown_only", true) + if (faceDownOnly) { + val isBlocked = getProximityStatus(service) + if (!isBlocked) return@launch + } + } - flashlightJob?.cancel() - flashlightJob = scope.launch { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + FlashlightUtil.isIntensitySupported(service, cameraId) + ) { val maxLevel = FlashlightUtil.getMaxLevel(service, cameraId) val pulseIntensity = prefs.getFloat("flashlight_pulse_max_intensity", 0.5f) val targetPulseLevel = (maxLevel * pulseIntensity).toInt().coerceAtLeast(1) @@ -394,12 +416,9 @@ class FlashlightHandler( ) isInternalToggle = false - } - } else { - // Fallback for older versions or devices without intensity support - Log.d("Flashlight", "Pulse fallback with cameraId: $cameraId") - flashlightJob?.cancel() - flashlightJob = scope.launch { + } else { + // Fallback for older versions or devices without intensity support + Log.d("Flashlight", "Pulse fallback with cameraId: $cameraId") isInternalToggle = true try { cameraManager.setTorchMode(cameraId, true) diff --git a/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt b/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt index 0ea641a9a..e91b10735 100644 --- a/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt +++ b/app/src/main/java/com/sameerasw/essentials/services/tiles/ScreenOffAccessibilityService.kt @@ -36,7 +36,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel -class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListener { +class ScreenOffAccessibilityService : AccessibilityService() { private val serviceScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) @@ -52,9 +52,6 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene private var screenReceiver: BroadcastReceiver? = null - // Proximity - private val sensorManager by lazy { getSystemService(SENSOR_SERVICE) as SensorManager } - private var proximitySensor: Sensor? = null // Freeze Logic private val freezeHandler = Handler(Looper.getMainLooper()) @@ -156,11 +153,6 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene } registerReceiver(screenReceiver, filter, RECEIVER_EXPORTED) - // Proximity - proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) - proximitySensor?.let { - sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_NORMAL) - } getSharedPreferences("essentials_prefs", MODE_PRIVATE) .registerOnSharedPreferenceChangeListener(preferenceChangeListener) @@ -213,7 +205,6 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene } catch (_: Exception) { } flashlightHandler.unregister() - sensorManager.unregisterListener(this) notificationLightingHandler.removeOverlay() ambientGlanceHandler.removeOverlay() aodForceTurnOffHandler.removeOverlay() @@ -237,17 +228,7 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene override fun onInterrupt() {} - override fun onSensorChanged(event: SensorEvent?) { - if (event?.sensor?.type == Sensor.TYPE_PROXIMITY) { - val distance = event.values[0] - val maxRange = event.sensor.maximumRange - val isBlocked = distance < maxRange && distance < 5f - - flashlightHandler.isProximityBlocked = isBlocked - } - } - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} override fun onConfigurationChanged(newConfig: android.content.res.Configuration) { super.onConfigurationChanged(newConfig) diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 13397b783..246f5dd05 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -1460,7 +1460,7 @@ Les spécifications inexactes utilisant GSMArena ont été retirées pour garder les informations de l\'appareil propres, rapides et entièrement fiables. Mises à jour d\'applis migrées Le menu Applications est maintenant entièrement intégré dans la fonctionnalité \"Votre Android\", rendant plus facile la gestion de toutes les mises à jour de vos applis dans un centre cohérent. - Favorite Features - View and launch your favorite features at a glance. - No favorite features pinned yet. Open the app to pin some! + Fonctionnalités préférées + Regardez et lancez vos fonctionnalités préférées en un coup d\'œil. + Pas encore de fonctionnalité préférée épinglée. Ouvrez l\'application pour en épingler ! diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index 44ab6a569..879cfb5c4 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -14,9 +14,9 @@ Empty screen off widget App Freezing Flashlight Pulse - Check for pre-releases + Cek Pra-rilis Mungkin tidak stabil - Default tab + Tab default Keamanan Hidupkan kunci aplikasi @@ -24,26 +24,26 @@ Konfirmasi untuk menghidupkan kunci aplikasi Konfirmasi untuk mematikan kunci aplikasi Pilih aplikasi dikunci - Choose which apps require authentication + Pilih aplikasi mana yang memerlukan autentikasi Lindungi aplikasi anda dengan Biometrik. aplikasi yang dikunci akan meminta kunci layar, ketika dibuka, Dan tetap terbuka hingga layar dimatikan. - Beware that this is not a robust solution as this is only a 3rd party application. If you need strong security, consider using Private Space or other such features. + Perlu diingat bahwa ini bukanlah solusi yang andal karena ini hanya aplikasi pihak ke3. Jika Anda membutuhkan keamanan yang kuat, pertimbangkan untuk menggunakan Ruang Pribadi atau fitur serupa lainnya. Pengingat, permintaan otentikasi biometrik hanya memungkinkan Anda menggunakan metode kelas keamanan KUAT. Metode keamanan buka kunci wajah dalam kelas LEMAH pada perangkat seperti Pixel 7 hanya akan dapat menggunakan metode otentikasi KUAT lainnya yang tersedia seperti sidik jari atau PIN. - Use usage access + Gunakan akses penggunaan Instead of accessibility (Freeze, App Lock, Dynamic Night Light) Hidupkan Remap tombol Gunakan Shizuku atau Root Berfungsi ketika layar mati (Rekomendasi) Shizuku tidak bekerja - Detected %1$s + Terdeteksi %1$s Status: %1$s Senter Opsi senter - Adjust fading and other settings + Sesuaikan pemudaran dan opsi lainnya Tema hitam murni - Use pure black background in dark mode - Haptic Feedback + Gunakan latarbelakang hitam murni di mode gelap + Umpan balik haptik Remap Long Press Layar mati Layar hidup @@ -51,23 +51,23 @@ Volume turun Toggle flashlight Media play/pause - Media next - Media previous - Toggle vibrate - Toggle mute - AI assistant + Media selanjutnya + Media sebelumnya + Alihkan getar + Alihkan pembisuan + Asisten AI Ambil Tangkapan layar Cycle sound modes Lingkari untuk menelusuri Like current song Like song settings - This feature requires notification access to detect the currently playing media and trigger the like action. Please enable it below. + Fitur ini memerlukan akses notifikasi untuk mendeteksi media yang sedang diputar dan memicu tindakan suka. Harap aktifkan di bawah ini. Show toast message - Show overlay on AOD + Tampilkan overlay di AOD Essentials On Display Glance at media and info on AOD - Docked mode - No timeout + Mode Didock + Tak ada batas Respect new notifications Briefly disable for new notifications Bentuk acak @@ -80,11 +80,11 @@ Notification glance Keep AOD on while notifications are pending Same apps as notification lighting - This feature will dynamically enable Always on Display when a notification arrives from a selected app, and disable it once all matching notifications are dismissed. Pick apps or use the same selection as notification lighting. + Fitur ini akan secara dinamis mengaktifkan Always on Display saat notifikasi tiba dari aplikasi yang dipilih, dan menonaktifkannya setelah semua notifikasi yang sesuai ditutup. Pilih aplikasi atau gunakan pilihan yang sama seperti pencahayaan notifikasi. Izinkan akses notifikasi Toggle media volume - When the screen is off, long-press the selected button to trigger its assigned action. On Pixel devices, this action only gets triggered if the AOD is on due to system limitations. - When the screen is on, long-press the selected button to trigger its assigned action. + Saat layar mati, tekan lama tombol yang dipilih untuk memicu tindakan yang ditugaskan. Pada perangkat Pixel, tindakan ini hanya dipicu jika Always On Display (AOD) aktif karena keterbatasan sistem. + Saat layar menyala, tekan lama tombol yang dipilih untuk mengaktifkan tindakan yang ditugaskan. Flashlight Intensity Fade in and out Smoothly toggle flashlight @@ -128,39 +128,39 @@ Pilih aplikasi Kontrol aplikasi - Freeze - Unfreeze + Bekukan + Pulihkan Hapus - Create shortcut - App info + Buat pintasan + Info aplikasi Apa itu pembekuan? App freezing disables the app\'s launch activity which removes it from the app list and updates. It will prevent the app from starting at all until it\'s unfrozen which saves resources but you will need to unfreeze from here or manually re-enable. JANGAN BEKUKAN APLIKASI KOMUNIKASI - What is Suspend? - Suspending an app used to pause the app activity and prevent background executions but with recent Android changes, it only pauses the notifications from appearing and that\'s pretty much it. But does allows you to unpause from the launcher app list as they will be still available as grayscale paused app icons. + Apa itu penangguhan? + Menangguhkan aplikasi dulunya menghentikan sementara aktivitas aplikasi dan mencegah eksekusi di latar belakang, tetapi dengan perubahan Android terbaru, fitur ini hanya menghentikan sementara munculnya notifikasi, dan hanya itu saja. Namun, fitur ini memungkinkan Anda untuk mengaktifkan kembali aplikasi dari daftar aplikasi peluncur karena aplikasi tersebut akan tetap tersedia sebagai ikon aplikasi yang dijeda berwarna abu-abu. Should work the same as the native app pause/ focus mode features. Opsi lainnya Bekukan semua aplikasi - Unfreeze all apps - Export frozen apps list - Import frozen apps list - Pick apps to freeze + Pulihkan semua aplikasi + Ekspor daftar aplikasi dibekukan + Impor daftar aplikasi dibekukan + Pilih aplikasi untuk dibekukan Choose which apps can be frozen Munculkan di peluncur - Add a launcher shortcut for easier access - Automation - Freeze when locked - Freeze delay - Immediate + Tambahkan pintasan peluncur untuk kemudahan mengakses + Automasi + Bekukan ketika mengunci + Jeda pembekuan + Langsung 1m 5m 15m Manual Auto freeze apps Freeze selected apps when the device locks. Choose a delay to avoid freezing apps if you unlock the screen shortly after turning it off. - Freezing system apps might be dangerous and may cause unexpected behavior. + Membekukan aplikasi sistem sangat berbahagia dan menimbulkan masalah tak terduga. Hidupkan di pengaturan - Don\'t freeze active apps + Jangan bekukan aplikasi yang aktif Akses penggunaan Required to detect which apps are currently in the foreground to avoid freezing them Required to detect foreground apps for App Lock features when accessibility is not used. @@ -168,7 +168,7 @@ Mode pembekuan Freezing App suspension - Can not switch mode while apps are frozen. Please unfreeze all and try again. + Tidak dapat berganti mode ketika aplikasi dibekukan. Mohon pulihkan semua dan coba lagi. Hanya tampilkan ketika layar mati Lewati notifikasi bisu diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index efe57c8f4..4a769dc02 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -60,7 +60,7 @@ Ses modlarını değiştir Aramak için Daire Şu anki şarkıyı beğen - Şarkıyı beğenme ayarları + Şarkı beğenme ayarları Bu özellik, o anda oynatılan medyayı algılamak ve benzer eylemi tetiklemek için bildirim erişimi gerektirir. Lütfen aşağıdan etkinleştirin. Tost mesajını göster AOD\'de kaplamayı göster @@ -357,7 +357,7 @@ Alttaki gezinme çubuğunu gizle Launcher\'da göster Dinamik olarak hareket çubuğunu yalnızca ana ekranda olduğunuzda gösterir - Aramak için daire hareketi + Aramak için Daire hareketi Aramak için Daire\'yi başlatmak için alt alana uzun basın Hareket bölgesi yüksekliği Alt kısımdaki dokunma alanının yüksekliğini ayarlar @@ -660,9 +660,9 @@ Devre dışı Uyarlanabilir Parlaklık Haritalar Güç Tasarrufu - Aramak + Ara Durmak - Aramak + Arama Dondurulmuş uygulamaları ara Geri @@ -868,7 +868,7 @@ Hepsini temizle Kilit ekranında yetkisiz ağ değişiklikleri yapılmaya çalışıldığında cihazın sabit olarak kilitlenmesi gerekir. Ayarlara erişmek için kimlik doğrulaması yapın - %1$s Ayarlar + %1$s Ayarları özellik ayarlar saklamak