summaryrefslogtreecommitdiff
path: root/app/src/main/java/foundation
diff options
context:
space:
mode:
authorjacquarg <guillaume.jacquart@hoodbrains.com>2021-11-07 22:43:32 +0100
committerjacquarg <guillaume.jacquart@hoodbrains.com>2021-11-12 13:13:23 +0100
commit5891ac8ea718dcb40014f5a90d2672f902eb6c55 (patch)
tree083007df227f0c91fed1c48ed81471074dae853f /app/src/main/java/foundation
parent2ea0e99f1bcec61e1a94bb7c35605fca49c53d1c (diff)
Update fake location
Diffstat (limited to 'app/src/main/java/foundation')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt27
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/data/repositories/CityDataSource.kt46
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt23
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt2
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt88
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt66
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt4
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt2
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt2
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt187
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt161
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt17
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/LocationApiDelegate.kt81
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt9
14 files changed, 302 insertions, 413 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index b73a79d..d8c3047 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -22,21 +22,22 @@ import android.content.Context
import android.os.Process
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
import foundation.e.privacycentralapp.domain.usecases.AppListUseCase
+import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase
import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase
+import foundation.e.privacycentralapp.dummy.CityDataSource
import foundation.e.privacycentralapp.dummy.TrackTrackersPrivacyMock
import foundation.e.privacycentralapp.features.dashboard.DashBoardViewModelFactory
import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyViewModelFactory
import foundation.e.privacycentralapp.features.location.FakeLocationViewModelFactory
-import foundation.e.privacycentralapp.features.location.LocationApiDelegate
import foundation.e.privacycentralapp.features.trackers.TrackersViewModelFactory
import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersViewModelFactory
import foundation.e.privacymodules.ipscrambler.IpScramblerModule
import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
-import foundation.e.privacymodules.location.FakeLocation
-import foundation.e.privacymodules.location.IFakeLocation
+import foundation.e.privacymodules.location.FakeLocationModule
+import foundation.e.privacymodules.location.IFakeLocationModule
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.ApplicationDescription
import foundation.e.trackerfilter.api.BlockTrackersPrivacyModule
@@ -54,7 +55,7 @@ class DependencyContainer constructor(val app: Application) {
val context: Context by lazy { app.applicationContext }
// Drivers
- private val fakeLocationModule: IFakeLocation by lazy { FakeLocation(app.applicationContext) }
+ private val fakeLocationModule: IFakeLocationModule by lazy { FakeLocationModule(app.applicationContext) }
private val permissionsModule by lazy { PermissionsPrivacyModule(app.applicationContext) }
private val ipScramblerModule: IIpScramblerModule by lazy { IpScramblerModule(app.applicationContext) }
@@ -67,10 +68,6 @@ class DependencyContainer constructor(val app: Application) {
)
}
- private val locationApi by lazy {
- LocationApiDelegate(fakeLocationModule, permissionsModule, appDesc)
- }
-
private val blockTrackersPrivacyModule by lazy { BlockTrackersPrivacyModule.getInstance(context) }
// Repositories
@@ -85,9 +82,8 @@ class DependencyContainer constructor(val app: Application) {
private val ipScramblingStateUseCase by lazy {
IpScramblingStateUseCase(ipScramblerModule, localStateRepository, GlobalScope)
}
- private val appListUseCase by lazy {
- AppListUseCase(permissionsModule, GlobalScope)
- }
+ private val appListUseCase = AppListUseCase(permissionsModule, GlobalScope)
+
private val trackersStatisticsUseCase by lazy {
TrackersStatisticsUseCase(trackTrackersPrivacyModule)
}
@@ -96,13 +92,20 @@ class DependencyContainer constructor(val app: Application) {
TrackersStateUseCase(blockTrackersPrivacyModule, trackTrackersPrivacyModule, permissionsModule, localStateRepository, GlobalScope)
}
+ private val fakeLocationStateUseCase by lazy {
+ FakeLocationStateUseCase(
+ fakeLocationModule, permissionsModule, localStateRepository, CityDataSource, appDesc, GlobalScope)
+ }
+
// ViewModelFactories
val dashBoardViewModelFactory by lazy {
DashBoardViewModelFactory(getQuickPrivacyStateUseCase, ipScramblingStateUseCase, trackersStatisticsUseCase, trackersStateUseCase)
}
val fakeLocationViewModelFactory by lazy {
- FakeLocationViewModelFactory(locationApi)
+ FakeLocationViewModelFactory(
+ getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
+ fakeLocationStateUseCase = fakeLocationStateUseCase)
}
val blockerService = BlockerInterface.getInstance(context)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/CityDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/CityDataSource.kt
new file mode 100644
index 0000000..5fab9b7
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/CityDataSource.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 E FOUNDATION
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.dummy
+
+object CityDataSource {
+ private val BARCELONA = Pair(41.3851f, 2.1734f)
+ private val BUDAPEST = Pair(47.4979f, 19.0402f)
+ private val ABU_DHABI = Pair(24.4539f, 54.3773f)
+ private val HYDERABAD = Pair(17.3850f, 78.4867f)
+ private val QUEZON_CITY = Pair(14.6760f, 121.0437f)
+ private val PARIS = Pair(48.8566f, 2.3522f)
+ private val LONDON = Pair(51.5074f, 0.1278f)
+ private val SHANGHAI = Pair(31.2304f, 121.4737f)
+ private val MADRID = Pair(40.4168f, 3.7038f)
+ private val LAHORE = Pair(31.5204f, 74.3587f)
+ private val CHICAGO = Pair(41.8781f, 87.6298f)
+
+ val citiesLocationsList = listOf(
+ BARCELONA,
+ BUDAPEST,
+ ABU_DHABI,
+ HYDERABAD,
+ QUEZON_CITY,
+ PARIS,
+ LONDON,
+ SHANGHAI,
+ MADRID,
+ LAHORE,
+ CHICAGO
+ )
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt
index 5f22c96..78cb4e4 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt
@@ -26,6 +26,8 @@ class LocalStateRepository(context: Context) {
private const val SHARED_PREFS_FILE = "localState"
private const val KEY_QUICK_PRIVACY = "quickPrivacy"
private const val KEY_IP_SCRAMBLING = "ipScrambling"
+ private const val KEY_FAKE_LATITUDE = "fakeLatitude"
+ private const val KEY_FAKE_LONGITUDE = "fakeLongitude"
}
private val sharedPref = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE)
@@ -40,6 +42,27 @@ class LocalStateRepository(context: Context) {
var quickPrivacyEnabledFlow: Flow<Boolean> = quickPrivacyEnabledMutableFlow
+ var fakeLocation: Pair<Float, Float>?
+ get() = if (sharedPref.contains(KEY_FAKE_LATITUDE) && sharedPref.contains(
+ KEY_FAKE_LONGITUDE))
+ Pair(
+ sharedPref.getFloat(KEY_FAKE_LATITUDE, 0f),
+ sharedPref.getFloat(KEY_FAKE_LONGITUDE, 0f))
+ else null
+ set(value) {
+ if (value == null) {
+ sharedPref.edit()
+ .remove(KEY_FAKE_LATITUDE)
+ .remove(KEY_FAKE_LONGITUDE)
+ .commit()
+ } else {
+ sharedPref.edit()
+ .putFloat(KEY_FAKE_LATITUDE, value.first)
+ .putFloat(KEY_FAKE_LONGITUDE, value.second)
+ .commit()
+ }
+ }
+
var isIpScramblingEnabled: Boolean
get() = sharedPref.getBoolean(KEY_IP_SCRAMBLING, false)
set(value) = set(KEY_IP_SCRAMBLING, value)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt
index dbb9b0a..35a77b3 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt
@@ -18,5 +18,5 @@
package foundation.e.privacycentralapp.domain.entities
enum class LocationMode {
- REAL_LOCATION, RANDOM_LOCATION, CUSTOM_LOCATION
+ REAL_LOCATION, RANDOM_LOCATION, SPECIFIC_LOCATION
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt
index 6d13f0e..02fdb0f 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt
@@ -17,47 +17,89 @@
package foundation.e.privacycentralapp.domain.usecases
-/*import android.app.AppOpsManager
-import android.content.Intent
-import android.util.Log
+import android.app.AppOpsManager
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
import foundation.e.privacycentralapp.domain.entities.LocationMode
-import foundation.e.privacycentralapp.features.location.LocationApiDelegate
-import foundation.e.privacymodules.location.IFakeLocation
+import foundation.e.privacycentralapp.dummy.CityDataSource
+import foundation.e.privacymodules.location.IFakeLocationModule
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.AppOpModes
import foundation.e.privacymodules.permissions.data.ApplicationDescription
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlin.random.Random
class FakeLocationStateUseCase(
- private val fakeLocationModule: IFakeLocation,
+ private val fakeLocationModule: IFakeLocationModule,
+
private val permissionsModule: PermissionsPrivacyModule,
private val localStateRepository: LocalStateRepository,
+ private val citiesRepository: CityDataSource,
private val appDesc: ApplicationDescription,
private val coroutineScope: CoroutineScope
) {
- private fun acquireLocationPermission() {
- try {
-
- permissionsModule.setAppOpMode(
- appDesc, AppOpsManager.OPSTR_COARSE_LOCATION,
- AppOpModes.ALLOWED
- )
- permissionsModule.setAppOpMode(
- appDesc, AppOpsManager.OPSTR_FINE_LOCATION,
- AppOpModes.ALLOWED
- )
- } catch (e: Exception) {
- // Log.e(TAG, "Can't start RealLocation", e)
+ init {
+ coroutineScope.launch {
+ localStateRepository.quickPrivacyEnabledFlow.collect {
+ applySettings(it, localStateRepository.fakeLocation)
+ }
}
}
- private fun applySettings(isQuickPrivacyEnabled: Boolean, fakeLocationMode: LocationMode) {
- when {
- // isQuickPrivacyEnabled ->
+ fun getLocationMode(): LocationMode = when(localStateRepository.fakeLocation) {
+ null -> LocationMode.REAL_LOCATION
+ in citiesRepository.citiesLocationsList -> LocationMode.RANDOM_LOCATION
+ else -> LocationMode.SPECIFIC_LOCATION
+ }
+
+ private fun acquireLocationPermission() {
+ permissionsModule.toggleDangerousPermission(appDesc,
+ android.Manifest.permission.ACCESS_FINE_LOCATION, true)
+
+ // permissionsModule.setAppOpMode(
+ // appDesc, AppOpsManager.OPSTR_COARSE_LOCATION,
+ // AppOpModes.ALLOWED
+ // )
+ // permissionsModule.setAppOpMode(
+ // appDesc, AppOpsManager.OPSTR_FINE_LOCATION,
+ // AppOpModes.ALLOWED
+ // )
+ }
+
+ private fun applySettings(isQuickPrivacyEnabled: Boolean, fakeLocation: Pair<Float, Float>?) {
+ if (isQuickPrivacyEnabled && fakeLocation != null) {
+ if (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) != AppOpModes.ALLOWED) {
+ permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED)
+ }
+ fakeLocationModule.startFakeLocation()
+
+ fakeLocationModule.setFakeLocation(fakeLocation.first.toDouble(), fakeLocation.second.toDouble())
+ } else {
+ fakeLocationModule.stopFakeLocation()
}
}
+ fun setSpecificLocation(latitude: Float, longitude: Float) {
+ setFakeLocation(latitude to longitude)
+ }
+
+ fun setRandomLocation() {
+ val randomIndex = Random.nextInt(citiesRepository.citiesLocationsList.size)
+ val location = citiesRepository.citiesLocationsList[randomIndex]
+
+ setFakeLocation(location)
+ }
+
+ private fun setFakeLocation(location: Pair<Float, Float>) {
+ localStateRepository.fakeLocation = location
+ applySettings(true, location)
+ }
+
+ fun stopFakeLocation() {
+ localStateRepository.fakeLocation = null
+ applySettings(true, null)
+ }
-}*/
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt
deleted file mode 100644
index 988c7f4..0000000
--- a/app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.privacycentralapp.dummy
-
-import foundation.e.privacycentralapp.domain.entities.LocationMode
-import kotlin.random.Random
-
-data class City(val name: String, val latitude: Double, val longitude: Double) {
-
- fun toRandomLocation(): Location {
- return Location(LocationMode.RANDOM_LOCATION, this.latitude, this.longitude)
- }
-}
-
-object CityDataSource {
- private val BARCELONA = Pair(41.3851, 2.1734)
- private val BUDAPEST = Pair(47.4979, 19.0402)
- private val ABU_DHABI = Pair(24.4539, 54.3773)
- private val HYDERABAD = Pair(17.3850, 78.4867)
- private val QUEZON_CITY = Pair(14.6760, 121.0437)
- private val PARIS = Pair(48.8566, 2.3522)
- private val LONDON = Pair(51.5074, 0.1278)
- private val SHANGHAI = Pair(31.2304, 121.4737)
- private val MADRID = Pair(40.4168, 3.7038)
- private val LAHORE = Pair(31.5204, 74.3587)
- private val CHICAGO = Pair(41.8781, 87.6298)
-
- // LatLong Array, the order should be the same as that of R.array.cities
- private val latLongArray = arrayOf(
- BARCELONA,
- BUDAPEST,
- ABU_DHABI,
- HYDERABAD,
- QUEZON_CITY,
- PARIS,
- LONDON,
- SHANGHAI,
- MADRID,
- LAHORE,
- CHICAGO
- )
-
- fun getRandomCity(cities: Array<String>): City {
- if (cities.size != latLongArray.size) {
- throw IllegalStateException("LatLong array must have the same number of element as in cities array.")
- }
- val randomIndex = Random.nextInt(cities.size)
- val latLong = latLongArray[randomIndex]
- return City(cities[randomIndex], latLong.first, latLong.second)
- }
-}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
index 246854b..38daeab 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
@@ -183,9 +183,9 @@ object DummyDataSource {
requireNotNull(location) { "Custom location should be null" }
_location.value = location
}
- LocationMode.CUSTOM_LOCATION -> {
+ LocationMode.SPECIFIC_LOCATION -> {
requireNotNull(location) { "Custom location should be null" }
- _location.value = location.copy(mode = LocationMode.CUSTOM_LOCATION)
+ _location.value = location.copy(mode = LocationMode.SPECIFIC_LOCATION)
}
}
return true
diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
index 04ee5bf..ab4ba72 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
@@ -24,5 +24,5 @@ import foundation.e.privacycentralapp.domain.entities.LocationMode
fun LocationMode.mapToString(context: Context): String = when (this) {
LocationMode.REAL_LOCATION -> context.getString(R.string.real_location_mode)
LocationMode.RANDOM_LOCATION -> context.getString(R.string.random_location_mode)
- LocationMode.CUSTOM_LOCATION -> context.getString(R.string.fake_location_mode)
+ LocationMode.SPECIFIC_LOCATION -> context.getString(R.string.fake_location_mode)
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
index 5185737..39b6138 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
@@ -56,7 +56,7 @@ class DashboardFeature(
val totalGraph: Int? = null,
// val graphData
val trackersCount: Int? = null,
- val dayTrackersCount: Int? = null,
+ val activeTrackersCount: Int? = null,
val dayStatistics: List<Int>? = null
)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt
index b16af28..7c6a715 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt
@@ -18,17 +18,17 @@
package foundation.e.privacycentralapp.features.location
import android.util.Log
-import com.mapbox.mapboxsdk.geometry.LatLng
import foundation.e.flowmvi.Actor
import foundation.e.flowmvi.Reducer
import foundation.e.flowmvi.SingleEventProducer
import foundation.e.flowmvi.feature.BaseFeature
import foundation.e.privacycentralapp.domain.entities.LocationMode
-import foundation.e.privacycentralapp.dummy.CityDataSource
-import foundation.e.privacycentralapp.dummy.DummyDataSource
-import foundation.e.privacycentralapp.dummy.Location
+import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
// Define a state machine for Fake location feature
class FakeLocationFeature(
@@ -46,7 +46,10 @@ class FakeLocationFeature(
singleEventProducer
) {
data class State(
- val location: Location
+ val isEnabled: Boolean,
+ val mode: LocationMode,
+ val specificLatitude: Float? = null,
+ val specificLongitude: Float? = null
)
sealed class SingleEvent {
@@ -57,153 +60,97 @@ class FakeLocationFeature(
}
sealed class Action {
+ object Init : Action()
// Action which is triggered everytime the location is updated.
- data class UpdateLocationAction(val latLng: LatLng) : Action()
- object UseRealLocationAction : Action()
- data class UseRandomLocationAction(
- val cities: Array<String>
- ) : Action() {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as UseRandomLocationAction
-
- if (!cities.contentEquals(other.cities)) return false
+ //data class UpdateLocationAction(val latLng: LatLng) : Action()
- return true
- }
-
- override fun hashCode(): Int {
- return cities.contentHashCode()
- }
- }
-
- object UseSpecificLocationAction : Action()
- data class SetCustomFakeLocationAction(
- val latitude: Double,
- val longitude: Double
- ) : Action()
+ object UseRealLocationAction : Action()
+ object UseRandomLocationAction : Action()
+ data class SetSpecificLocationAction(
+ val latitude: Float,
+ val longitude: Float) : Action()
}
sealed class Effect {
- data class LocationUpdatedEffect(val latitude: Double, val longitude: Double) : Effect()
- object RealLocationSelectedEffect : Effect()
- object RandomLocationSelectedEffect : Effect()
- object SpecificLocationSelectedEffect : Effect()
- object SpecificLocationSavedEffect : Effect()
+ data class QuickPrivacyUpdatedEffect(val isEnabled: Boolean): Effect()
+ data class LocationModeUpdatedEffect(
+ val mode: LocationMode,
+ val latitude: Float? = null,
+ val longitude: Float? = null) : Effect()
data class ErrorEffect(val message: String) : Effect()
+ object QuickPrivacyDisabledWarningEffect : Effect()
}
companion object {
fun create(
initialState: State = State(
- Location(
- LocationMode.REAL_LOCATION,
- 0.0,
- 0.0
- )
+ isEnabled = false,
+ mode = LocationMode.REAL_LOCATION
),
- coroutineScope: CoroutineScope,
- locationApi: LocationApiDelegate
+ getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ fakeLocationStateUseCase: FakeLocationStateUseCase,
+ coroutineScope: CoroutineScope
) = FakeLocationFeature(
initialState, coroutineScope,
reducer = { state, effect ->
when (effect) {
- Effect.RandomLocationSelectedEffect -> state.copy(
- location = state.location.copy(
- mode = LocationMode.RANDOM_LOCATION
- )
- )
- Effect.RealLocationSelectedEffect -> state.copy(
- location = state.location.copy(
- mode = LocationMode.REAL_LOCATION
- )
- )
- is Effect.ErrorEffect, Effect.SpecificLocationSavedEffect -> state
- is Effect.LocationUpdatedEffect -> state.copy(
- location = state.location.copy(
- latitude = effect.latitude,
- longitude = effect.longitude
- )
- )
- is Effect.SpecificLocationSelectedEffect -> state.copy(
- location = state.location.copy(
- mode = LocationMode.CUSTOM_LOCATION
- )
- )
+ is Effect.QuickPrivacyUpdatedEffect -> state.copy(isEnabled = effect.isEnabled)
+ is Effect.LocationModeUpdatedEffect -> state.copy(
+ mode = effect.mode,
+ specificLatitude = effect.latitude,
+ specificLongitude = effect.longitude)
+
+ is Effect.ErrorEffect,
+ Effect.QuickPrivacyDisabledWarningEffect -> state
}
},
- actor = { _, action ->
+ actor = { state, action ->
when (action) {
- is Action.UpdateLocationAction -> flowOf(
- Effect.LocationUpdatedEffect(
- action.latLng.latitude,
- action.latLng.longitude
- )
- )
- is Action.SetCustomFakeLocationAction -> {
- val location = Location(
- LocationMode.CUSTOM_LOCATION,
- action.latitude,
- action.longitude
- )
- locationApi.setFakeLocation(action.latitude, action.longitude)
- val success = DummyDataSource.setLocationMode(
- LocationMode.CUSTOM_LOCATION,
- location
- )
- if (success) {
- flowOf(
- Effect.SpecificLocationSavedEffect
+ is Action.Init -> merge(
+ getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow.map { Effect.QuickPrivacyUpdatedEffect(it) },
+ flowOf(Effect.LocationModeUpdatedEffect(fakeLocationStateUseCase.getLocationMode())))
+
+ // is Action.UpdateLocationAction -> flowOf(
+ // Effect.LocationUpdatedEffect(
+ // action.latLng.latitude,
+ // action.latLng.longitude
+ // )
+ // )
+
+ is Action.SetSpecificLocationAction -> {
+ if (state.isEnabled) {
+ fakeLocationStateUseCase.setSpecificLocation(
+ action.latitude,
+ action.longitude
)
- } else {
flowOf(
- Effect.ErrorEffect("Couldn't select location")
+ Effect.LocationModeUpdatedEffect(
+ mode = LocationMode.SPECIFIC_LOCATION,
+ latitude = action.latitude,
+ longitude = action.longitude
+ )
)
- }
+ } else flowOf(Effect.QuickPrivacyDisabledWarningEffect)
}
is Action.UseRandomLocationAction -> {
- val randomCity = CityDataSource.getRandomCity(action.cities)
- locationApi.setFakeLocation(randomCity.latitude, randomCity.longitude)
- val success = DummyDataSource.setLocationMode(
- LocationMode.RANDOM_LOCATION,
- randomCity.toRandomLocation()
- )
- if (success) {
- flowOf(
- Effect.RandomLocationSelectedEffect
- )
- } else {
- flowOf(
- Effect.ErrorEffect("Couldn't select location")
- )
- }
+ if (state.isEnabled) {
+ fakeLocationStateUseCase.setRandomLocation()
+ flowOf(Effect.LocationModeUpdatedEffect(LocationMode.RANDOM_LOCATION))
+ } else flowOf(Effect.QuickPrivacyDisabledWarningEffect)
}
is Action.UseRealLocationAction -> {
- locationApi.startRealLocation()
- val success = DummyDataSource.setLocationMode(LocationMode.REAL_LOCATION)
- if (success) {
- flowOf(
- Effect.RealLocationSelectedEffect
- )
- } else {
- flowOf(
- Effect.ErrorEffect("Couldn't select location")
- )
- }
- }
- is Action.UseSpecificLocationAction -> {
- flowOf(Effect.SpecificLocationSelectedEffect)
+ if (state.isEnabled) {
+ fakeLocationStateUseCase.stopFakeLocation()
+ flowOf(Effect.LocationModeUpdatedEffect(LocationMode.REAL_LOCATION))
+ } else flowOf(Effect.QuickPrivacyDisabledWarningEffect)
}
}
},
singleEventProducer = { _, _, effect ->
when (effect) {
- Effect.RandomLocationSelectedEffect -> SingleEvent.RandomLocationSelectedEvent
- Effect.SpecificLocationSavedEffect -> SingleEvent.SpecificLocationSavedEvent
- Effect.RealLocationSelectedEffect -> SingleEvent.RealLocationSelectedEvent
+ Effect.QuickPrivacyDisabledWarningEffect ->
+ SingleEvent.ErrorEvent("Enabled Quick Privacy to use functionalities")
is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
else -> null
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt
index bdc405e..0f69808 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt
@@ -28,7 +28,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
-import android.widget.RadioButton
import android.widget.Toast
import androidx.annotation.NonNull
import androidx.core.view.isVisible
@@ -62,6 +61,7 @@ import foundation.e.privacycentralapp.common.NavToolbarFragment
import foundation.e.privacycentralapp.databinding.FragmentFakeLocationBinding
import foundation.e.privacycentralapp.domain.entities.LocationMode
import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf
+import foundation.e.privacycentralapp.features.location.FakeLocationFeature.Action
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
@@ -93,11 +93,13 @@ class FakeLocationFragment :
private var inputJob: Job? = null
+ private var displayedLocation: Pair<Float, Float>? = null
// Callback which updates the map in realtime.
private val locationChangeCallback: LocationEngineCallback<LocationEngineResult> =
object : LocationEngineCallback<LocationEngineResult> {
override fun onSuccess(result: LocationEngineResult?) {
result?.lastLocation?.let {
+ displayedLocation = it.latitude.toFloat() to it.longitude.toFloat()
mapboxMap.locationComponent.forceLocationUpdate(
LocationUpdate.Builder().location(it).animationDuration(100)
.build()
@@ -114,16 +116,16 @@ class FakeLocationFragment :
}
// Only update location when location mode is set to real location or random location.
// It basically triggers a UI update.
- if (viewModel.fakeLocationFeature.state.value.location.mode != LocationMode.CUSTOM_LOCATION) {
- viewModel.submitAction(
- FakeLocationFeature.Action.UpdateLocationAction(
- LatLng(
- it.latitude,
- it.longitude
- )
- )
- )
- }
+ // if (viewModel.fakeLocationFeature.state.value.location.mode != LocationMode.SPECIFIC_LOCATION) {
+ // viewModel.submitAction(
+ // FakeLocationFeature.Action.UpdateLocationAction(
+ // LatLng(
+ // it.latitude,
+ // it.longitude
+ // )
+ // )
+ // )
+ // }
}
}
@@ -148,29 +150,13 @@ class FakeLocationFragment :
lifecycleScope.launchWhenStarted {
viewModel.fakeLocationFeature.singleEvents.collect { event ->
when (event) {
- is FakeLocationFeature.SingleEvent.RandomLocationSelectedEvent -> {
- displayToast("Random location selected")
- hoveringMarker?.visibility = View.GONE
- isCameraMoved = false
- }
- is FakeLocationFeature.SingleEvent.SpecificLocationSavedEvent -> {
- // Hide camera hover marker when custom location is picked from map.
- displayToast("Specific location selected")
- hoveringMarker?.visibility = View.GONE
- isCameraMoved = false
- }
is FakeLocationFeature.SingleEvent.ErrorEvent -> {
displayToast(event.error)
- isCameraMoved = false
- }
- FakeLocationFeature.SingleEvent.RealLocationSelectedEvent -> {
- displayToast("Real location selected")
- hoveringMarker?.visibility = View.GONE
- isCameraMoved = false
}
}
}
}
+ lifecycleScope.launchWhenStarted { viewModel.submitAction(Action.Init) }
}
override fun onAttach(context: Context) {
@@ -206,25 +192,29 @@ class FakeLocationFragment :
binding.mapView.addView(hoveringMarker)
hoveringMarker?.visibility = View.GONE // Keep hovering marker hidden by default
- mapboxMap.addOnCameraMoveStartedListener {
- // Show marker when user starts to move across the map.
- hoveringMarker?.visibility = if (binding.mapView.isEnabled) {
- View.VISIBLE
- } else {
- View.GONE
- }
- isCameraMoved = true
- }
-
- mapboxMap.addOnCameraMoveListener {
- if (binding.mapView.isEnabled) {
- viewModel.submitAction(
- FakeLocationFeature.Action.UpdateLocationAction(
- mapboxMap.cameraPosition.target
- )
- )
- }
- }
+ // mapboxMap.addOnCameraMoveStartedListener {
+ // // Show marker when user starts to move across the map.
+ // hoveringMarker?.visibility = if (binding.mapView.isEnabled) {
+ // View.VISIBLE
+ // } else {
+ // View.GONE
+ // }
+ // isCameraMoved = true
+ // }
+ //
+ // mapboxMap.addOnCameraMoveListener {
+ // if (binding.mapView.isEnabled) {
+ // mapboxMap.cameraPosition.target.let {
+ // viewModel.submitAction(
+ // Action.SetSpecificLocationAction(
+ // it.latitude.toFloat(),
+ // it.longitude.toFloat()
+ // )
+ // )
+ // }
+ // }
+
+ // }
// Bind click listeners once map is ready.
bindClickListeners()
}
@@ -238,8 +228,8 @@ class FakeLocationFragment :
delay(DEBOUNCE_PERIOD)
ensureActive()
try {
- val value = editable.toString().toDouble()
- val maxValue = if (isLat) 90.0 else 180.0
+ val value = editable.toString().toFloat()
+ val maxValue = if (isLat) 90f else 180f
if (value > maxValue || value < -maxValue) {
throw NumberFormatException("value $value is out of bounds")
@@ -251,12 +241,10 @@ class FakeLocationFragment :
// Here, value is valid, try to send the values
try {
- val lat = binding.edittextLatitude.text.toString().toDouble()
- val lon = binding.edittextLongitude.text.toString().toDouble()
- if (lat <= 90.0 && lat >= -90.0 && lon <= 180.0 && lon >= -180.0) {
- viewModel.submitAction(
- FakeLocationFeature.Action.SetCustomFakeLocationAction(lat, lon)
- )
+ val lat = binding.edittextLatitude.text.toString().toFloat()
+ val lon = binding.edittextLongitude.text.toString().toFloat()
+ if (lat <= 90f && lat >= -90f && lon <= 180f && lon >= -180f) {
+ viewModel.submitAction(Action.SetSpecificLocationAction(lat, lon))
}
} catch (e: NumberFormatException) {}
} catch (e: NumberFormatException) {
@@ -268,14 +256,16 @@ class FakeLocationFragment :
}
private fun bindClickListeners() {
- binding.radioUseRealLocation.setOnClickListener { radioButton ->
- toggleLocationType(radioButton)
+ binding.radioUseRealLocation.setOnClickListener {
+ viewModel.submitAction(Action.UseRealLocationAction)
}
- binding.radioUseRandomLocation.setOnClickListener { radioButton ->
- toggleLocationType(radioButton)
+ binding.radioUseRandomLocation.setOnClickListener {
+ viewModel.submitAction(Action.UseRandomLocationAction)
}
- binding.radioUseSpecificLocation.setOnClickListener { radioButton ->
- toggleLocationType(radioButton)
+ binding.radioUseSpecificLocation.setOnClickListener {
+ viewModel.submitAction(
+Action.SetSpecificLocationAction(displayedLocation?.first?: 0f, displayedLocation?.second?: 0f)
+ )
}
binding.edittextLatitude.addTextChangedListener(
@@ -295,50 +285,25 @@ class FakeLocationFragment :
)
}
- private fun toggleLocationType(radioButton: View?) {
- if (radioButton is RadioButton) {
- val checked = radioButton.isChecked
- when (radioButton.id) {
- R.id.radio_use_real_location ->
- if (checked) {
- viewModel.submitAction(
- FakeLocationFeature.Action.UseRealLocationAction
- )
- }
- R.id.radio_use_random_location ->
- if (checked) {
- viewModel.submitAction(
- FakeLocationFeature.Action.UseRandomLocationAction(
- resources.getStringArray(R.array.cities)
- )
- )
- }
- R.id.radio_use_specific_location ->
- if (checked) {
- viewModel.submitAction(
- FakeLocationFeature.Action.UseSpecificLocationAction
- )
- }
- }
- }
- }
-
override fun render(state: FakeLocationFeature.State) {
- binding.radioUseRandomLocation.isChecked = (state.location.mode == LocationMode.RANDOM_LOCATION)
+ hoveringMarker?.visibility = View.GONE
+ isCameraMoved = false
+
+ binding.radioUseRandomLocation.isChecked = (state.mode == LocationMode.RANDOM_LOCATION)
binding.radioUseSpecificLocation.isChecked =
- (state.location.mode == LocationMode.CUSTOM_LOCATION)
- binding.radioUseRealLocation.isChecked = (state.location.mode == LocationMode.REAL_LOCATION)
+ (state.mode == LocationMode.SPECIFIC_LOCATION)
+ binding.radioUseRealLocation.isChecked = (state.mode == LocationMode.REAL_LOCATION)
- binding.mapView.isEnabled = (state.location.mode == LocationMode.CUSTOM_LOCATION)
+ binding.mapView.isEnabled = (state.mode == LocationMode.SPECIFIC_LOCATION)
- binding.textlayoutLatitude.isVisible = (state.location.mode == LocationMode.CUSTOM_LOCATION)
- binding.textlayoutLongitude.isVisible = (state.location.mode == LocationMode.CUSTOM_LOCATION)
+ binding.textlayoutLatitude.isVisible = (state.mode == LocationMode.SPECIFIC_LOCATION)
+ binding.textlayoutLongitude.isVisible = (state.mode == LocationMode.SPECIFIC_LOCATION)
- binding.edittextLatitude.setText(state.location.latitude.toString())
- binding.edittextLongitude.setText(state.location.longitude.toString())
+ binding.edittextLatitude.setText(state.specificLatitude?.toString())
+ binding.edittextLongitude.setText(state.specificLongitude?.toString())
}
- override fun actions(): Flow<FakeLocationFeature.Action> = viewModel.actions
+ override fun actions(): Flow<Action> = viewModel.actions
@SuppressLint("MissingPermission")
private fun enableLocationPlugin(@NonNull loadedMapStyle: Style) {
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt
index b73c228..70ee0c1 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt
@@ -20,17 +20,24 @@ package foundation.e.privacycentralapp.features.location
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import foundation.e.privacycentralapp.common.Factory
+import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
-class FakeLocationViewModel(private val locationApi: LocationApiDelegate) : ViewModel() {
+class FakeLocationViewModel(
+ private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ private val fakeLocationStateUseCase: FakeLocationStateUseCase) : ViewModel() {
private val _actions = MutableSharedFlow<FakeLocationFeature.Action>()
val actions = _actions.asSharedFlow()
val fakeLocationFeature: FakeLocationFeature by lazy {
- FakeLocationFeature.create(coroutineScope = viewModelScope, locationApi = locationApi)
+ FakeLocationFeature.create(
+ getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
+ fakeLocationStateUseCase = fakeLocationStateUseCase,
+ coroutineScope = viewModelScope)
}
fun submitAction(action: FakeLocationFeature.Action) {
@@ -40,8 +47,10 @@ class FakeLocationViewModel(private val locationApi: LocationApiDelegate) : View
}
}
-class FakeLocationViewModelFactory(private val locationApi: LocationApiDelegate) : Factory<FakeLocationViewModel> {
+class FakeLocationViewModelFactory(
+ private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ private val fakeLocationStateUseCase: FakeLocationStateUseCase) : Factory<FakeLocationViewModel> {
override fun create(): FakeLocationViewModel {
- return FakeLocationViewModel((locationApi))
+ return FakeLocationViewModel(getQuickPrivacyStateUseCase, fakeLocationStateUseCase)
}
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/LocationApiDelegate.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/LocationApiDelegate.kt
deleted file mode 100644
index 88fef3e..0000000
--- a/app/src/main/java/foundation/e/privacycentralapp/features/location/LocationApiDelegate.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2021 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.privacycentralapp.features.location
-
-import android.app.AppOpsManager
-import android.util.Log
-import foundation.e.privacymodules.location.IFakeLocation
-import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
-import foundation.e.privacymodules.permissions.data.AppOpModes
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-
-class LocationApiDelegate(
- private val fakeLocationModule: IFakeLocation,
- private val permissionsModule: PermissionsPrivacyModule,
- private val appDesc: ApplicationDescription
-) {
-
- private val TAG = LocationApiDelegate::class.simpleName
-
- fun setFakeLocation(latitude: Double, longitude: Double) {
- if (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) != AppOpModes.ALLOWED) {
- permissionsModule.setAppOpMode(
- appDesc, AppOpsManager.OPSTR_MOCK_LOCATION,
- AppOpModes.ALLOWED
- )
- }
- try {
- fakeLocationModule.startFakeLocation()
- } catch (e: Exception) {
- Log.e(TAG, "Can't startFakeLocation", e)
- }
- fakeLocationModule.setFakeLocation(latitude, longitude)
- }
-
- fun stopFakeLocation() {
- try {
- permissionsModule.setAppOpMode(
- appDesc, AppOpsManager.OPSTR_MOCK_LOCATION,
- AppOpModes.IGNORED
- )
- permissionsModule.setAppOpMode(
- appDesc, AppOpsManager.OPSTR_MOCK_LOCATION,
- AppOpModes.IGNORED
- )
- fakeLocationModule.stopFakeLocation()
- } catch (e: Exception) {
- Log.e(TAG, "Can't stop FakeLocation", e)
- }
- }
-
- fun startRealLocation() {
- stopFakeLocation()
- try {
- permissionsModule.setAppOpMode(
- appDesc, AppOpsManager.OPSTR_COARSE_LOCATION,
- AppOpModes.ALLOWED
- )
- permissionsModule.setAppOpMode(
- appDesc, AppOpsManager.OPSTR_FINE_LOCATION,
- AppOpModes.ALLOWED
- )
- } catch (e: Exception) {
- Log.e(TAG, "Can't start RealLocation", e)
- }
- }
-}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
index 03784a0..ce7edd3 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
@@ -29,6 +29,7 @@ import foundation.e.privacycentralapp.features.dashboard.DashboardFragment
import foundation.e.trackerfilter.DNSBlockerService
import foundation.e.trackerfilter.StatsIntentService
+
open class MainActivity : FragmentActivity(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -44,15 +45,15 @@ open class MainActivity : FragmentActivity(R.layout.activity_main) {
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
handleIntent(intent)
+ // TODO move into tracker module.
+ AndroidEnvironment.initEnvironment(this)
+ startService(Intent(this, DNSBlockerService::class.java))
+ startService(Intent(this, StatsIntentService::class.java))
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleIntent(intent)
-
- AndroidEnvironment.initEnvironment(this)
- startService(Intent(this, DNSBlockerService::class.java))
- startService(Intent(this, StatsIntentService::class.java))
}
open fun handleIntent(intent: Intent) {}