Skip to content

Commit

Permalink
Rewrite actual logic on flows
Browse files Browse the repository at this point in the history
  • Loading branch information
AChep committed Nov 15, 2020
1 parent 023d4ab commit c07fbfa
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 222 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.artemchep.pocketmode.sensors

import android.content.Context
import com.artemchep.pocketmode.ext.isKeyguardLocked
import com.artemchep.pocketmode.models.Keyguard
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn

private const val KEYGUARD_CHECK_INTERVAL = 300L

/**
* @author Artem Chepurnoy
*/
fun flowOfKeyguard(
context: Context
): Flow<Keyguard> = flow<Keyguard> {
while (true) {
val keyguard = when (context.isKeyguardLocked()) {
true -> Keyguard.Locked
false -> Keyguard.Unlocked
}
emit(keyguard)
delay(KEYGUARD_CHECK_INTERVAL)
}
}
.flowOn(Dispatchers.Main)
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,21 @@ import kotlinx.coroutines.flow.*
/**
* @author Artem Chepurnoy
*/
@Suppress("FunctionName")
fun LockScreenEventFlow(
proximityLiveData: LiveData<Proximity?>,
screenLiveData: LiveData<Screen?>,
fun flowOfLockScreen(
proximityFlow: Flow<Proximity>,
screenFlow: Flow<Screen>,
phoneCallLiveData: LiveData<PhoneCall>,
keyguardLiveData: LiveData<Keyguard>
keyguardFlow: Flow<Keyguard>
): Flow<LockScreenEvent> {
val proximityFlow = proximityLiveData.asFlow().filterNotNull().distinctUntilChanged()
val screenFlow = screenLiveData.asFlow().filterNotNull().distinctUntilChanged()
val phoneCallFlow = phoneCallLiveData.asFlow().distinctUntilChanged()
val keyguardFlow = keyguardLiveData.asFlow().distinctUntilChanged()
return screenFlow
.distinctUntilChanged()
// If the screen is on -> subscribe to the
// keyguard flow.
.flatMapLatest { screen ->
when (screen) {
is Screen.On -> combine(
keyguardFlow,
keyguardFlow.distinctUntilChanged(),
phoneCallFlow,
) { keyguard, phoneCall ->
when {
Expand All @@ -54,9 +51,11 @@ fun LockScreenEventFlow(
}
}
.flatMapLatest { it }
.distinctUntilChanged()
.flatMapLatest { isActive ->
if (isActive) {
proximityFlow
.distinctUntilChanged()
.flatMapLatest { proximity ->
when (proximity) {
is Proximity.Far -> flowOf(Idle)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.artemchep.pocketmode.sensors

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.core.content.getSystemService
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import com.artemchep.pocketmode.util.observerFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMap

fun flowOfProximity(
context: Context,
): Flow<Float> = observerFlow<Float> { callback ->
val sensorManager = context.getSystemService<SensorManager>()
?: return@observerFlow {}

val sensorProximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
val sensorListener = object : SensorEventListener {
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
// Unused.
}

override fun onSensorChanged(event: SensorEvent) {
val distance = event.values[0]
callback(distance)
}
}

val delay = SensorManager.SENSOR_DELAY_NORMAL
sensorManager.registerListener(sensorListener, sensorProximity, delay)

return@observerFlow {
sensorManager.unregisterListener(sensorListener)
}
}

@Deprecated(
message = "Flow analogue is 'flowOfProximity'",
replaceWith = ReplaceWith("flowOfProximity(context)"),
)
@Suppress("FunctionName")
fun ProximityLiveData(
context: Context
): LiveData<Float> = flowOfProximity(context).asLiveData()
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,45 @@ import androidx.core.content.getSystemService
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.artemchep.pocketmode.models.Proximity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/**
* @author Artem Chepurnoy
*/
@Suppress("FunctionName")
fun flowOfBinaryProximity(
context: Context,
proximitySensor: Flow<Float> = flowOfProximity(context),
): Flow<Proximity> {
val proximityBinaryTransformation = proximityBinaryTransformationFactory(context)
return proximitySensor
.map {
proximityBinaryTransformation(it)
}
}

@Deprecated(
message = "Flow analogue is 'flowOfBinaryProximity'",
replaceWith = ReplaceWith("flowOfBinaryProximity(context, proximitySensor)"),
)
@Suppress("FunctionName")
fun ProximityBinaryLiveData(
context: Context,
proximitySensor: LiveData<Float?>,
): LiveData<Proximity?> {
proximitySensor: LiveData<Float>,
): LiveData<Proximity> {
val proximityBinaryTransformation = proximityBinaryTransformationFactory(context)
return Transformations.map(proximitySensor, proximityBinaryTransformation)
}

private fun proximityBinaryTransformationFactory(
context: Context,
): (distance: Float?) -> Proximity? {
): (distance: Float) -> Proximity {
val sensorProximityMaxRange = context.getSystemService<SensorManager>()
?.getDefaultSensor(Sensor.TYPE_PROXIMITY)
?.maximumRange ?: 1.0f
return transform@{ distance ->
distance?.let { proximityBinaryTransformationFactory(it, sensorProximityMaxRange) }
proximityBinaryTransformationFactory(distance, sensorProximityMaxRange)
}
}

Expand Down
66 changes: 66 additions & 0 deletions app/src/main/java/com/artemchep/pocketmode/sensors/FlowOfScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.artemchep.pocketmode.sensors

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.artemchep.pocketmode.ext.isScreenOn
import com.artemchep.pocketmode.models.Screen
import com.artemchep.pocketmode.util.observerFlow
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

private const val SCREEN_ON_CHECK_INTERVAL = 600L

fun flowOfScreen(
context: Context,
): Flow<Screen> = observerFlow<Screen> { callback ->
val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val screen = isScreenOn(context)
callback(screen)
}
}
val intentFilter = IntentFilter()
.apply {
addAction(Intent.ACTION_SCREEN_OFF)
addAction(Intent.ACTION_SCREEN_ON)
}
context.registerReceiver(broadcastReceiver, intentFilter)

val screen = isScreenOn(context)
callback(screen)
return@observerFlow {
context.unregisterReceiver(broadcastReceiver)
}
}
.flatMapLatest { screen ->
when (screen) {
is Screen.On -> flow {
emit(screen)

// While the screen is on, send the update every
// few seconds. This is needed because of the
// Always On mode which may not send the 'screen is off'
// broadcast.
while (true) {
delay(SCREEN_ON_CHECK_INTERVAL)
val newScreen = isScreenOn(context)
emit(newScreen)
}
}
else -> flowOf(screen)
}
}
.flowOn(Dispatchers.Main)
.distinctUntilChanged()

/**
* It does not store the screen state, it
* retrieves it every time.
*/
private fun isScreenOn(context: Context): Screen =
when (context.isScreenOn()) {
true -> Screen.On
false -> Screen.Off
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit c07fbfa

Please sign in to comment.