Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple fix #32

Closed
wants to merge 3 commits into from
Closed
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
8 changes: 2 additions & 6 deletions buildSrc/src/main/kotlin/Projects.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package lighthouse

import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.Lint
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.TestExtension
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import com.vanniktech.maven.publish.SonatypeHost
Expand All @@ -19,9 +16,7 @@ import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
/**
*
*/

fun Project.setupLibraryModule(
moduleName: String,
shouldBePublished: Boolean,
Expand Down Expand Up @@ -97,6 +92,7 @@ fun Project.setupDemoModule(
applicationId = name
versionCode = 1
versionName = project.versionName
targetSdk = project.targetSdk
}
buildFeatures {
compose = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class MainActivity : ComponentActivity() {
viewModel.stopDiscovery()
isDiscoveryRunning.value = false
},
enabled = !isDiscoveryRunning.value
enabled = isDiscoveryRunning.value
) {
Text(text = "Stop discovery")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ivanempire.lighthouse.LighthouseClient
import com.ivanempire.lighthouse.models.devices.AbridgedMediaDevice
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

class MainActivityViewModel(
private val lighthouseClient: LighthouseClient,
Expand All @@ -21,11 +23,17 @@ class MainActivityViewModel(
val discoveredDevices = backingDiscoveredDevices.asStateFlow()

fun startDiscovery() {
discoveryJob =
viewModelScope.launch {
lighthouseClient.discoverDevices().collect { backingDiscoveredDevices.value = it }
}
viewModelScope.launch {
discoveryJob =
lighthouseClient
.discoverDevices()
.flowOn(Dispatchers.IO)
.onEach { backingDiscoveredDevices.value = it }
.launchIn(viewModelScope)
}
}

fun stopDiscovery() = runBlocking { discoveryJob?.cancelAndJoin() }
fun stopDiscovery() {
discoveryJob?.cancel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ package com.ivanempire.lighthouse

import android.content.Context
import android.net.wifi.WifiManager
import com.ivanempire.lighthouse.core.LighthouseState
import com.ivanempire.lighthouse.core.RealDiscoveryManager
import com.ivanempire.lighthouse.core.RealLighthouseClient
import com.ivanempire.lighthouse.models.Constants.DEFAULT_SEARCH_REQUEST
import com.ivanempire.lighthouse.models.devices.AbridgedMediaDevice
import com.ivanempire.lighthouse.models.search.SearchRequest
import com.ivanempire.lighthouse.socket.RealSocketListener
import java.lang.IllegalStateException
import kotlinx.coroutines.flow.Flow

/** The main entrypoint for the Lighthouse library */
Expand All @@ -20,8 +17,6 @@ interface LighthouseClient {

private var retryCount = 1

private var shouldPersist = false

private var logger: LighthouseLogger? = null

private val wifiManager =
Expand All @@ -38,8 +33,6 @@ interface LighthouseClient {
this.retryCount += retryCount
}

fun setShouldPersist(shouldPersist: Boolean) = apply { this.shouldPersist = shouldPersist }

/**
* Specify a custom implementation of [LighthouseLogger] in order to log events from the
* library at the consumer level
Expand All @@ -51,14 +44,10 @@ interface LighthouseClient {
fun build(): LighthouseClient {
val socketListener = RealSocketListener(wifiManager, retryCount, logger)

val discoveryManager =
RealDiscoveryManager(
shouldPersist,
LighthouseState(logger),
socketListener,
logger,
)
return RealLighthouseClient(discoveryManager, logger = logger)
return RealLighthouseClient(
socketListener = socketListener,
logger = logger,
)
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,12 @@ internal class LighthouseState(private val logger: LighthouseLogger? = null) {
}

/**
* Iterates over the entire device list and filters out any stale devices. A stale device is
* defined as one that has not seen a media packet in the last [AbridgedMediaDevice.cache]
* seconds
* Iterates over the entire device list and removes any stale devices. A stale device is defined
* as one that has not seen a media packet in the last [AbridgedMediaDevice.cache] seconds
*
* @return A device list with stale root devices removed
*/
fun parseStaleDevices() {
fun removeStaleDevices() {
backingDeviceList.update { currentList ->
currentList.filterNot {
System.currentTimeMillis() - it.latestTimestamp > it.cache * 1000
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,66 @@ import com.ivanempire.lighthouse.LighthouseClient
import com.ivanempire.lighthouse.LighthouseLogger
import com.ivanempire.lighthouse.models.devices.AbridgedMediaDevice
import com.ivanempire.lighthouse.models.search.SearchRequest
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import com.ivanempire.lighthouse.parsers.DatagramPacketTransformer
import com.ivanempire.lighthouse.parsers.packets.MediaPacketParser
import com.ivanempire.lighthouse.socket.SocketListener
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive

/** Specific implementation of [LighthouseClient] */
internal class RealLighthouseClient(
private val discoveryManager: DiscoveryManager,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
private val logger: LighthouseLogger? = null,
private val socketListener: SocketListener,
private val logger: LighthouseLogger?,
) : LighthouseClient {

private val discoveryMutex = Mutex()
private var isDiscoveryRunning = false

override suspend fun discoverDevices(
searchRequest: SearchRequest
searchRequest: SearchRequest,
): Flow<List<AbridgedMediaDevice>> {
discoveryMutex.withLock {
if (isDiscoveryRunning) {
throw IllegalStateException(
"Discovery is already in progress - did you call discoverDevices() multiple times?"
)
}
isDiscoveryRunning = true
}
val state = LighthouseState(logger)

logger?.logStatusMessage(TAG, "Discovering devices with search request: $searchRequest")

val foundDevicesFlow = discoveryManager.createNewDeviceFlow(searchRequest)
val lostDevicesFlow = discoveryManager.createStaleDeviceFlow()
val foundDevicesFlow = createNewDeviceFlow(searchRequest, state)
val withoutStaleDevicesFlow = createNonStaleDeviceFlow(state)

return merge(foundDevicesFlow, lostDevicesFlow)
.distinctUntilChanged()
.flowOn(dispatcher)
.onCompletion { discoveryMutex.withLock { isDiscoveryRunning = false } }
return merge(foundDevicesFlow, withoutStaleDevicesFlow).distinctUntilChanged()
}

@OptIn(ExperimentalCoroutinesApi::class)
private fun createNewDeviceFlow(
searchRequest: SearchRequest,
lighthouseState: LighthouseState,
): Flow<List<AbridgedMediaDevice>> {
return socketListener
.listenForPackets(searchRequest)
.mapNotNull { DatagramPacketTransformer(it, logger) }
.mapNotNull { MediaPacketParser(it, logger) }
.onEach { lighthouseState.parseMediaPacket(it) }
.flatMapLatest { lighthouseState.deviceList }
.filter { it.isNotEmpty() }
}

private fun createNonStaleDeviceFlow(
lighthouseState: LighthouseState
): Flow<List<AbridgedMediaDevice>> {
return flow {
while (currentCoroutineContext().isActive) {
delay(1000)
lighthouseState.removeStaleDevices()
emit(lighthouseState.deviceList.value)
}
}
.filter { it.isNotEmpty() }
}

private companion object {
Expand Down
Loading