From 14e9da5757943c9b9431fa9d6643b0d9db1fdb21 Mon Sep 17 00:00:00 2001 From: konstantin Date: Fri, 26 Jan 2024 20:44:48 +0200 Subject: [PATCH 1/5] Update tags colors --- app/build.gradle.kts | 4 ++-- .../java/f/cking/software/utils/graphic/ComposeFunctions.kt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e47f673..a7d85a1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,8 +26,8 @@ android { // This version code will be applied only for F-Droid builds, for other builds version code will be generated by gradle dynamically // For some reason F-Droid requires version code to be hardcoded in the build.gradle file - versionCode = 1705859021 - versionName = "0.22.6-beta" + versionCode = 1705859023 + versionName = "0.22.7-beta" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt b/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt index 4262fc0..ff1ea8e 100644 --- a/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt +++ b/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt @@ -81,6 +81,7 @@ import org.osmdroid.views.MapView import java.time.LocalDate import java.time.LocalTime import kotlin.math.abs +import kotlin.random.Random @Composable fun rememberDateDialog( @@ -507,7 +508,7 @@ private val colorsDark = listOf( @Composable fun colorByHash(hash: Int): Color { val colors = if (isSystemInDarkTheme()) colorsDark else colorsLight - return colors[abs(hash % colors.size)] + return colors[abs(Random(hash).nextInt() % colors.size)] } @Composable From 4bf3ceeb6955bcc398d1563f1bcb24a9e61c6782 Mon Sep 17 00:00:00 2001 From: konstantin Date: Sat, 10 Feb 2024 13:15:19 +0200 Subject: [PATCH 2/5] Update code style and error messaging --- .../data/helpers/BleScanErrorMapper.kt | 2 +- .../domain/interactor/InteractorsModule.kt | 2 +- .../interactor/filterchecker/FilterChecker.kt | 3 +- .../f/cking/software/service/BgScanService.kt | 95 ++++++++++--------- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/f/cking/software/data/helpers/BleScanErrorMapper.kt b/app/src/main/java/f/cking/software/data/helpers/BleScanErrorMapper.kt index 78fe27a..7719f78 100644 --- a/app/src/main/java/f/cking/software/data/helpers/BleScanErrorMapper.kt +++ b/app/src/main/java/f/cking/software/data/helpers/BleScanErrorMapper.kt @@ -7,7 +7,7 @@ object BleScanErrorMapper { fun map(errorCode: Int): String { return when (errorCode) { ScanCallback.SCAN_FAILED_ALREADY_STARTED -> "Scan already started" - ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED -> "Application registration failed" + ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED -> "Application registration failed. Possible solution is to restart the device." ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED -> "Feature unsupported" ScanCallback.SCAN_FAILED_INTERNAL_ERROR -> "Internal error" ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES -> "Out of hardware resources" diff --git a/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt b/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt index df3466e..d60757a 100644 --- a/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt +++ b/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt @@ -26,7 +26,7 @@ object InteractorsModule { factory { RestoreDatabaseInteractor(get(), get(), get()) } factory { SaveRadarProfile(get(), get()) } factory { DeleteRadarProfile(get(), get()) } - factory { CheckDeviceLocationHistoryInteractor(get()) } + single { CheckDeviceLocationHistoryInteractor(get()) } factory { CheckUserLocationHistoryInteractor(get()) } factory { AddTagToDeviceInteractor(get(), get()) } factory { RemoveTagFromDeviceInteractor(get()) } diff --git a/app/src/main/java/f/cking/software/domain/interactor/filterchecker/FilterChecker.kt b/app/src/main/java/f/cking/software/domain/interactor/filterchecker/FilterChecker.kt index d4ef038..cda0604 100644 --- a/app/src/main/java/f/cking/software/domain/interactor/filterchecker/FilterChecker.kt +++ b/app/src/main/java/f/cking/software/domain/interactor/filterchecker/FilterChecker.kt @@ -17,7 +17,6 @@ abstract class FilterChecker( if (useCache() && cacheValue != null && System.currentTimeMillis() - cacheValue.time < powerModeHelper.powerMode(useCached = true).filterCacheExpirationTime ) { - // Timber.d("Cache hit for $key") return cacheValue.value } val result = checkInternal(deviceData, filter) @@ -39,6 +38,6 @@ abstract class FilterChecker( ) companion object { - private const val MAX_CACHE_SIZE = 5000 + private const val MAX_CACHE_SIZE = 5_000 } } \ No newline at end of file diff --git a/app/src/main/java/f/cking/software/service/BgScanService.kt b/app/src/main/java/f/cking/software/service/BgScanService.kt index 303b8b9..85e7f68 100644 --- a/app/src/main/java/f/cking/software/service/BgScanService.kt +++ b/app/src/main/java/f/cking/software/service/BgScanService.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import timber.log.Timber +import java.util.concurrent.atomic.AtomicInteger class BgScanService : Service() { @@ -43,7 +44,7 @@ class BgScanService : Service() { private val saveReportInteractor: SaveReportInteractor by inject() private val handler = Handler(Looper.getMainLooper()) - private var failureScanCounter: Int = 0 + private var failureScanCounter: AtomicInteger = AtomicInteger(0) private var locationDisabledWasReported: Boolean = false private var bluetoothDisabledWasReported: Boolean = false private val nextScanRunnable = Runnable { @@ -68,12 +69,10 @@ class BgScanService : Service() { } private fun handleError(exception: Throwable) { - failureScanCounter++ - reportError(exception) - if (failureScanCounter >= MAX_FAILURE_SCANS_TO_CLOSE) { - reportError(RuntimeException("Ble Scan service was stopped after $MAX_FAILURE_SCANS_TO_CLOSE errors")) + if (failureScanCounter.incrementAndGet() >= MAX_FAILURE_SCANS_TO_CLOSE) { + reportError(RuntimeException("Ble Scan service has been stopped after $MAX_FAILURE_SCANS_TO_CLOSE errors")) stopSelf() } else { scheduleNextScan() @@ -105,7 +104,7 @@ class BgScanService : Service() { reportError(IllegalStateException("BLE Service is started but permissins are not granted")) stopSelf() }, - onPermissionGranted = { + onPermissionGranted = { locationProvider.startLocationFetching() scan() } @@ -133,7 +132,7 @@ class BgScanService : Service() { private fun scan() { scope.launch { try { - bleScannerHelper.scan(scanListener = bleListener,) + bleScannerHelper.scan(scanListener = bleListener) } catch (e: BleScannerHelper.BluetoothIsNotInitialized) { handleBleIsTurnedOffError() notificationsHelper.updateNotification( @@ -150,45 +149,12 @@ class BgScanService : Service() { private fun handleScanResult(batch: List) { scope.launch { - - val notificationContent: NotificationsHelper.ServiceNotificationContent = - if (batch.isEmpty() && !locationProvider.isLocationAvailable() && !locationDisabledWasReported) { - notificationsHelper.notifyLocationIsTurnedOff() - reportError(IllegalStateException("The BLE scanner did not return anything. This may happen if geolocation is turned off at the system level. Location access is required to work with BLE on Android.")) - locationDisabledWasReported = true - NotificationsHelper.ServiceNotificationContent.LocationIsTurnedOff - } else if (batch.isEmpty() && !bleScannerHelper.isBluetoothEnabled()) { - handleBleIsTurnedOffError() - NotificationsHelper.ServiceNotificationContent.BluetoothIsTurnedOff - } else if (batch.isNotEmpty()) { - locationDisabledWasReported = false - bluetoothDisabledWasReported = false - - - try { - val analyseResult = analyseScanBatchInteractor.execute(batch) - withContext(Dispatchers.Default) { - saveScanBatchInteractor.execute(batch) - } - - withContext(Dispatchers.Main) { - handleAnalysResult(analyseResult) - } - - failureScanCounter = 0 - - if (analyseResult.knownDevicesCount > 0) { - NotificationsHelper.ServiceNotificationContent.KnownDevicesAround(analyseResult.knownDevicesCount) - } else { - NotificationsHelper.ServiceNotificationContent.TotalDevicesAround(batch.size) - } - } catch (exception: Throwable) { - handleError(exception) - NotificationsHelper.ServiceNotificationContent.NoDataYet - } - } else { - NotificationsHelper.ServiceNotificationContent.NoDataYet - } + val notificationContent: NotificationsHelper.ServiceNotificationContent = when { + batch.isEmpty() && !locationProvider.isLocationAvailable() && !locationDisabledWasReported -> handleLocationDisabled() + batch.isEmpty() && !bleScannerHelper.isBluetoothEnabled() -> handleBleIsTurnedOffError() + batch.isNotEmpty() -> handleNonEmptyBatch(batch) + else -> NotificationsHelper.ServiceNotificationContent.NoDataYet + } notificationsHelper.updateNotification(notificationContent, createCloseServiceIntent(this@BgScanService)) @@ -196,12 +162,47 @@ class BgScanService : Service() { } } - private fun handleBleIsTurnedOffError() { + private fun handleLocationDisabled(): NotificationsHelper.ServiceNotificationContent { + notificationsHelper.notifyLocationIsTurnedOff() + reportError(IllegalStateException("The BLE scanner did not return anything. This may happen if geolocation is turned off at the system level. Location access is required to work with BLE on Android.")) + locationDisabledWasReported = true + return NotificationsHelper.ServiceNotificationContent.LocationIsTurnedOff + } + + private fun handleBleIsTurnedOffError(): NotificationsHelper.ServiceNotificationContent { if (!bluetoothDisabledWasReported) { notificationsHelper.notifyBluetoothIsTurnedOff() reportError(BleScannerHelper.BluetoothIsNotInitialized()) bluetoothDisabledWasReported = true } + return NotificationsHelper.ServiceNotificationContent.BluetoothIsTurnedOff + } + + private suspend fun handleNonEmptyBatch(batch: List): NotificationsHelper.ServiceNotificationContent { + locationDisabledWasReported = false + bluetoothDisabledWasReported = false + + return try { + val analyseResult = analyseScanBatchInteractor.execute(batch) + withContext(Dispatchers.Default) { + saveScanBatchInteractor.execute(batch) + } + + withContext(Dispatchers.Main) { + handleAnalysResult(analyseResult) + } + + failureScanCounter.set(0) + + if (analyseResult.knownDevicesCount > 0) { + NotificationsHelper.ServiceNotificationContent.KnownDevicesAround(analyseResult.knownDevicesCount) + } else { + NotificationsHelper.ServiceNotificationContent.TotalDevicesAround(batch.size) + } + } catch (exception: Throwable) { + handleError(exception) + NotificationsHelper.ServiceNotificationContent.NoDataYet + } } private fun handleProfileCheckingResult(profiles: List) { From a7558981228eba1e52f85ad3da111d9ce9fe6c8b Mon Sep 17 00:00:00 2001 From: konstantin Date: Sat, 10 Feb 2024 13:40:28 +0200 Subject: [PATCH 3/5] Add filters description --- .../f/cking/software/ui/filter/FilterType.kt | 32 +++++++++---------- .../ui/filter/SelectFilterTypeScreen.kt | 30 +++++++++++++---- app/src/main/res/values/strings.xml | 15 +++++++++ 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/f/cking/software/ui/filter/FilterType.kt b/app/src/main/java/f/cking/software/ui/filter/FilterType.kt index 5d19aa3..0d5229c 100644 --- a/app/src/main/java/f/cking/software/ui/filter/FilterType.kt +++ b/app/src/main/java/f/cking/software/ui/filter/FilterType.kt @@ -3,20 +3,20 @@ package f.cking.software.ui.filter import androidx.annotation.StringRes import f.cking.software.R -enum class FilterType(@StringRes val displayNameRes: Int) { - NAME(R.string.filter_by_name), - ADDRESS(R.string.filter_by_address), - BY_FIRST_DETECTION(R.string.filter_by_first_detection_period), - BY_LAST_DETECTION(R.string.filter_by_last_detection_period), - BY_IS_FAVORITE(R.string.filter_by_is_favorite), - BY_MANUFACTURER(R.string.filter_by_manufacturer), - BY_MIN_DETECTION_TIME(R.string.filter_by_min_lost_period), - AIRDROP_CONTACT(R.string.filter_apple_airdrop_contact), - IS_FOLLOWING(R.string.filter_device_is_following_me), - BY_DEVICE_LOCATION(R.string.filter_device_location), - BY_USER_LOCATION(R.string.filter_user_location), - BY_TAG(R.string.filter_by_tag), - BY_LOGIC_ANY(R.string.filter_any_of), - BY_LOGIC_ALL(R.string.filter_all_of), - BY_LOGIC_NOT(R.string.filter_not), +enum class FilterType(@StringRes val displayNameRes: Int, @StringRes val displayDescription: Int) { + BY_LOGIC_ANY(R.string.filter_any_of, R.string.filter_any_of_description), + BY_LOGIC_ALL(R.string.filter_all_of, R.string.filter_all_of_description), + BY_LOGIC_NOT(R.string.filter_not, R.string.filter_not_description), + NAME(R.string.filter_by_name, R.string.filter_by_name_description), + ADDRESS(R.string.filter_by_address, R.string.filter_by_address_description), + BY_TAG(R.string.filter_by_tag, R.string.filter_by_tag_description), + BY_MIN_DETECTION_TIME(R.string.filter_by_min_lost_period, R.string.filter_by_min_lost_period_description), + BY_FIRST_DETECTION(R.string.filter_by_first_detection_period, R.string.filter_by_first_detection_period_description), + BY_LAST_DETECTION(R.string.filter_by_last_detection_period, R.string.filter_by_last_detection_period_description), + BY_IS_FAVORITE(R.string.filter_by_is_favorite, R.string.filter_by_is_favorite_description), + BY_MANUFACTURER(R.string.filter_by_manufacturer, R.string.filter_by_manufacturer_description), + IS_FOLLOWING(R.string.filter_device_is_following_me, R.string.filter_device_is_following_me_description), + BY_DEVICE_LOCATION(R.string.filter_device_location, R.string.filter_device_location_description), + BY_USER_LOCATION(R.string.filter_user_location, R.string.filter_user_location_description), + AIRDROP_CONTACT(R.string.filter_apple_airdrop_contact, R.string.filter_apple_airdrop_contact_description), } \ No newline at end of file diff --git a/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt b/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt index 94a3c8c..4ee3f37 100644 --- a/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt +++ b/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt @@ -2,7 +2,10 @@ package f.cking.software.ui.filter import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.MaterialTheme @@ -11,6 +14,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.vanpra.composematerialdialogs.MaterialDialogState @@ -27,7 +31,10 @@ object SelectFilterTypeScreen { ThemedDialog( dialogState = dialogState, buttons = { - negativeButton(stringResource(R.string.cancel), textStyle = TextStyle(color = MaterialTheme.colorScheme.onSurface)) { dialogState.hide() } + negativeButton( + stringResource(R.string.cancel), + textStyle = TextStyle(color = MaterialTheme.colorScheme.onSurface) + ) { dialogState.hide() } } ) { LazyColumn { @@ -50,11 +57,22 @@ object SelectFilterTypeScreen { .fillMaxWidth() .clickable { onClickListener.invoke() } ) { - Text( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), - text = stringResource(item.displayNameRes), - fontSize = 18.sp - ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + Text( + text = stringResource(item.displayNameRes), + fontSize = 18.sp + ) + Spacer(modifier = Modifier.height(2.dp)) + Text( + text = stringResource(item.displayDescription), + fontSize = 14.sp, + fontWeight = FontWeight.Light, + ) + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dc74662..0b0b34e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -97,20 +97,35 @@ Select filter By name + Triggers when the device name matches By address + Triggers when the device address matches By first detection period + Triggers if the found device was first time detected in the selected period By last detection period + Triggers if the found device was last time detected in the selected period By is favorite + Triggers if the device is marked as a favorite By manufacturer + Triggers when the device manufacturer matches By min lost period + Triggers if the device was lost for the selected period. Might be helpful to avoid constant profile detection if the matched device is online for a long time Apple airdrop contact + [DEPRECATED] Triggers when the device is an Apple device and the airdrop contact matches Device is following me + Triggers if the device is following you during your move for a selected period of time. Any of + Logical operator OR with group of filters inside All of + Logical operator AND with group of filters inside Not + Logical operator NOT By device location + Triggers when the device has been previously seen in the particular location in the selected time period By your location + Triggers if you are currently in a particular location. It might be helpful to avoid some filters triggering if you are at home or at the office By tag + Triggers if the device has a selected tag Create new From 5692c83b1de5a3931e7c273c3cf3ff02dd2fbbab Mon Sep 17 00:00:00 2001 From: konstantin Date: Sat, 10 Feb 2024 13:43:49 +0200 Subject: [PATCH 4/5] Fix date range filter --- app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt b/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt index 3482fba..322c7fe 100644 --- a/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt +++ b/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt @@ -412,7 +412,7 @@ object FilterScreen { Spacer(modifier = Modifier.width(2.dp)) ClearIcon { filter.toDate = null - filter.toDate = null + filter.toTime = null } } } From a2a65b93a44802e5a33538222bcd5cab10e4f2a1 Mon Sep 17 00:00:00 2001 From: konstantin Date: Sat, 10 Feb 2024 13:45:40 +0200 Subject: [PATCH 5/5] Rollback InteractorsModule --- .../f/cking/software/domain/interactor/InteractorsModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt b/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt index d60757a..df3466e 100644 --- a/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt +++ b/app/src/main/java/f/cking/software/domain/interactor/InteractorsModule.kt @@ -26,7 +26,7 @@ object InteractorsModule { factory { RestoreDatabaseInteractor(get(), get(), get()) } factory { SaveRadarProfile(get(), get()) } factory { DeleteRadarProfile(get(), get()) } - single { CheckDeviceLocationHistoryInteractor(get()) } + factory { CheckDeviceLocationHistoryInteractor(get()) } factory { CheckUserLocationHistoryInteractor(get()) } factory { AddTagToDeviceInteractor(get(), get()) } factory { RemoveTagFromDeviceInteractor(get()) }