Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Boolean> { 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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())

Expand All @@ -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())
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -213,7 +205,6 @@ class ScreenOffAccessibilityService : AccessibilityService(), SensorEventListene
} catch (_: Exception) {
}
flashlightHandler.unregister()
sensorManager.unregisterListener(this)
notificationLightingHandler.removeOverlay()
ambientGlanceHandler.removeOverlay()
aodForceTurnOffHandler.removeOverlay()
Expand All @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/res/values-fr-rFR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1460,7 +1460,7 @@
<string name="whats_new_specs_desc">Les spécifications inexactes utilisant GSMArena ont été retirées pour garder les informations de l\'appareil propres, rapides et entièrement fiables.</string>
<string name="whats_new_apps_title">Mises à jour d\'applis migrées</string>
<string name="whats_new_apps_desc">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.</string>
<string name="favorites_widget_name">Favorite Features</string>
<string name="favorites_widget_description">View and launch your favorite features at a glance.</string>
<string name="favorites_widget_empty_state">No favorite features pinned yet. Open the app to pin some!</string>
<string name="favorites_widget_name">Fonctionnalités préférées</string>
<string name="favorites_widget_description">Regardez et lancez vos fonctionnalités préférées en un coup d\'œil.</string>
<string name="favorites_widget_empty_state">Pas encore de fonctionnalité préférée épinglée. Ouvrez l\'application pour en épingler !</string>
</resources>
Loading