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
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ android {
minSdk = 29
targetSdk = 36

versionCode = 1708536378
versionName = "0.31.1-beta"
versionCode = 1708536379
versionName = "0.31.2-beta"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/f/cking/software/data/DataMappers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fun LocationEntity.toDomain(): LocationModel {
return LocationModel(lat, lng, time)
}

fun DeviceEntity.toDomain(appleAirDrop: AppleAirDrop?): DeviceData {
fun DeviceEntity.toDomain(appleAirDrop: AppleAirDrop? = null): DeviceData {
return DeviceData(
address = address,
name = name,
Expand Down
13 changes: 11 additions & 2 deletions app/src/main/java/f/cking/software/data/database/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ abstract class AppDatabase : RoomDatabase() {
}
}

suspend fun getDatabaseSize(context: Context): Long {
return withContext(Dispatchers.IO) {
val dbFile = File(context.getDatabasePath(openHelper.databaseName).toString())
dbFile.length()
}
}

private fun testDatabase(name: String, context: Context) {
val testDb = build(context, name)
testDb.openHelper.writableDatabase.isDatabaseIntegrityOk
Expand Down Expand Up @@ -232,10 +239,12 @@ abstract class AppDatabase : RoomDatabase() {
)
""".trimIndent()
)
it.execSQL("""
it.execSQL(
"""
CREATE INDEX IF NOT EXISTS index_profile_detect_profile_id_trigger_time
ON profile_detect(profile_id, trigger_time)
""".trimIndent())
""".trimIndent()
)
}

private fun migration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ interface AppleContactDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(contacts: List<AppleContactEntity>)

@Query("DELETE FROM apple_contacts WHERE associated_address IN (:addresses)")
fun deleteAllByAddresses(addresses: List<String>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import f.cking.software.data.database.entity.DeviceEntity
import kotlinx.coroutines.flow.Flow

@Dao
interface DeviceDao {

@Query("SELECT * FROM device")
fun getAll(): List<DeviceEntity>

@Query("SELECT * FROM device")
fun observeAll(): Flow<List<DeviceEntity>>

@Query("SELECT * FROM device ORDER BY last_detect_time_ms DESC LIMIT :limit OFFSET :offset")
fun getPaginated(offset: Int, limit: Int): List<DeviceEntity>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import f.cking.software.data.database.entity.JournalEntryEntity
import kotlinx.coroutines.flow.Flow

@Dao
interface JournalDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(journalEntryEntity: JournalEntryEntity)

@Query("SELECT * FROM journal")
fun observe(): Flow<List<JournalEntryEntity>>

@Query("SELECT * FROM journal")
fun getAll(): List<JournalEntryEntity>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.room.OnConflictStrategy
import androidx.room.Query
import f.cking.software.data.database.entity.DeviceToLocationEntity
import f.cking.software.data.database.entity.LocationEntity
import kotlinx.coroutines.flow.Flow

@Dao
interface LocationDao {
Expand All @@ -22,6 +23,9 @@ interface LocationDao {
""")
fun getAllLocationsByDeviceAddress(address: String, fromTime: Long = 0, toTime: Long = Long.MAX_VALUE): List<LocationEntity>

@Query("SELECT * FROM location")
fun observeAllLocations(): Flow<List<LocationEntity>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveLocation(locationEntity: LocationEntity)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,23 @@ import androidx.room.Query
import f.cking.software.data.database.entity.LocationEntity
import f.cking.software.data.database.entity.ProfileDetectEntity
import f.cking.software.data.database.entity.RadarProfileEntity
import kotlinx.coroutines.flow.Flow

@Dao
interface RadarProfileDao {

@Query("SELECT * FROM radar_profile")
fun getAll(): List<RadarProfileEntity>

@Query("SELECT * FROM radar_profile")
fun observe(): Flow<List<RadarProfileEntity>>

@Query("SELECT * FROM radar_profile WHERE id = :id")
fun getById(id: Int): RadarProfileEntity?

@Query("SELECT * FROM radar_profile WHERE id IN (:ids)")
fun getAllById(ids: List<Int>): List<RadarProfileEntity>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(radarProfile: RadarProfileEntity)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class BleFiltersProvider(

suspend fun getKnownDevicesFilters(): List<ScanFilter> {
return withContext(Dispatchers.Default) {
val allKnownDevices = getAllDevicesInteractor.execute()
val allKnownDevices = getAllDevicesInteractor.execute(withAirdropInfo = false)

val lastSeenDevices = allKnownDevices
.sortedByDescending { it.lastDetectTimeMs }
Expand Down
81 changes: 46 additions & 35 deletions app/src/main/java/f/cking/software/data/repo/DevicesRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import f.cking.software.domain.toDomain
import f.cking.software.splitToBatches
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

Expand All @@ -23,17 +25,18 @@ class DevicesRepository(
private val deviceDao: DeviceDao = appDatabase.deviceDao()
private val appleContactsDao = appDatabase.appleContactDao()
private val lastBatch = MutableStateFlow(emptyList<DeviceData>())
private val allDevices = MutableStateFlow(emptyList<DeviceData>())
private val allDevices = deviceDao.observeAll()
.map { it.toDomain(withAirdropInfo = true) }

suspend fun getDevices(): List<DeviceData> {
suspend fun getDevices(withAirdropInfo: Boolean = false): List<DeviceData> {
return withContext(Dispatchers.IO) {
deviceDao.getAll().toDomainWithAirDrop()
deviceDao.getAll().toDomain(withAirdropInfo)
}
}

suspend fun getPaginated(offset: Int, limit: Int): List<DeviceData> {
return withContext(Dispatchers.IO) {
deviceDao.getPaginated(offset, limit).toDomainWithAirDrop()
deviceDao.getPaginated(offset, limit).toDomain(withAirdropInfo = true)
}
}

Expand All @@ -45,7 +48,7 @@ class DevicesRepository(
emptyList()
} else {
val scanTime = lastDevice.lastDetectTimeMs
deviceDao.getByLastDetectTime(scanTime).toDomainWithAirDrop()
deviceDao.getByLastDetectTime(scanTime).toDomain(withAirdropInfo = true)
}
}
}
Expand All @@ -54,18 +57,14 @@ class DevicesRepository(
lastBatch.value = emptyList()
}

suspend fun observeAllDevices(): StateFlow<List<DeviceData>> {
return allDevices.apply {
if (allDevices.value.isEmpty()) {
notifyListeners()
}
}
fun observeAllDevices(): Flow<List<DeviceData>> {
return allDevices
}

suspend fun observeLastBatch(): StateFlow<List<DeviceData>> {
return lastBatch.apply {
if (lastBatch.value.isEmpty()) {
notifyListeners()
notifyLastBatchListener()
}
}
}
Expand All @@ -74,22 +73,22 @@ class DevicesRepository(
withContext(Dispatchers.IO) {
saveDevices(devices)
saveContacts(devices)
notifyListeners()
notifyLastBatchListener()
}
}

suspend fun saveDevice(data: DeviceData) {
withContext(Dispatchers.IO) {
deviceDao.insert(data.toData())
notifyListeners()
notifyLastBatchListener()
}
}

suspend fun saveFollowingDetection(device: DeviceData, detectionTime: Long) {
withContext(Dispatchers.IO) {
val new = device.copy(lastFollowingDetectionTimeMs = detectionTime)
deviceDao.insert(new.toData())
notifyListeners()
notifyLastBatchListener()
}
}

Expand All @@ -98,14 +97,25 @@ class DevicesRepository(
addresses.splitToBatches(DatabaseUtils.getMaxSQLVariablesNumber()).forEach { addressesBatch ->
deviceDao.deleteAllByAddress(addressesBatch)
}
notifyListeners()
notifyLastBatchListener()
}
}

suspend fun clearUnAssociatedAirdrops() {
withContext(Dispatchers.IO) {
val allDevices = deviceDao.getAll().mapTo(mutableSetOf()) { it.address }
val allAidrops = appleContactsDao.getAll().map { it.associatedAddress }

val unassotiatedAirdrops = allAidrops.filter { !allDevices.contains(it) }

appleContactsDao.deleteAllByAddresses(unassotiatedAirdrops)
}
}

suspend fun getAllByAddresses(addresses: List<String>): List<DeviceData> {
return withContext(Dispatchers.IO) {
addresses.splitToBatches(DatabaseUtils.getMaxSQLVariablesNumber()).flatMap {
deviceDao.findAllByAddresses(addresses).toDomainWithAirDrop()
deviceDao.findAllByAddresses(addresses).toDomain(withAirdropInfo = true)
}
}
}
Expand Down Expand Up @@ -147,16 +157,12 @@ class DevicesRepository(
}
}

private suspend fun notifyListeners() {
private suspend fun notifyLastBatchListener() {
coroutineScope {
launch {
launch(Dispatchers.Default) {
val data = getLastBatch()
lastBatch.emit(data)
}
launch {
val data = getDevices()
allDevices.emit(data)
}
}
}

Expand All @@ -167,21 +173,26 @@ class DevicesRepository(
}
}

private suspend fun List<DeviceEntity>.toDomainWithAirDrop(): List<DeviceData> {
return withContext(Dispatchers.IO) {
private suspend fun List<DeviceEntity>.toDomain(withAirdropInfo: Boolean): List<DeviceData> {
return withContext(Dispatchers.Default) {
if (withAirdropInfo) {
toDomainWithAirDrop()
} else {
map { it.toDomain() }
}
}
}

val allRelatedContacts =
splitToBatches(DatabaseUtils.getMaxSQLVariablesNumber()).flatMap { batch ->
appleContactsDao.getByAddresses(batch.map { it.address })
}
private suspend fun List<DeviceEntity>.toDomainWithAirDrop(): List<DeviceData> {
return withContext(Dispatchers.Default) {
val allRelatedContacts = withContext(Dispatchers.IO) {
appleContactsDao.getAll().groupBy { it.associatedAddress }
}

map { device ->
val airdrop = allRelatedContacts.asSequence()
.filter { it.associatedAddress == device.address }
.map { it.toDomain() }
.toList()
.takeIf { it.isNotEmpty() }
?.let { AppleAirDrop(it) }
val airdrop = allRelatedContacts[device.address]?.let {
AppleAirDrop(it.map { it.toDomain() })
}

device.toDomain(airdrop)
}
Expand Down
27 changes: 10 additions & 17 deletions app/src/main/java/f/cking/software/data/repo/JournalRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,26 @@ import f.cking.software.domain.toData
import f.cking.software.domain.toDomain
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

class JournalRepository(
private val database: AppDatabase,
) {
class JournalRepository(database: AppDatabase) {

val journalDao = database.journalDao()
private val journal = MutableStateFlow(emptyList<JournalEntry>())

suspend fun observe(): Flow<List<JournalEntry>> {
return journal.apply {
if (journal.value.isEmpty()) {
notifyListeners()
private val journalDao = database.journalDao()
private val journal = journalDao.observe()
.map {
withContext(Dispatchers.Default) {
it.map { it.toDomain() }
}
}

fun observe(): Flow<List<JournalEntry>> {
return journal
}

suspend fun newEntry(journalEntry: JournalEntry) {
withContext(Dispatchers.IO) {
journalDao.insert(journalEntry.toData())
notifyListeners()
}
}

Expand All @@ -42,9 +40,4 @@ class JournalRepository(
journalDao.getById(id)?.toDomain()
}
}

private suspend fun notifyListeners() {
val data = getAllEntries()
journal.emit(data)
}
}
Loading