summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt8
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt13
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt123
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt11
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt2
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt12
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt76
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt209
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt9
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt1
-rw-r--r--app/src/main/res/layout/fragment_fake_location.xml38
-rw-r--r--app/src/main/res/values/colors.xml1
12 files changed, 370 insertions, 133 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index d8c3047..5bd7c08 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -94,18 +94,20 @@ class DependencyContainer constructor(val app: Application) {
private val fakeLocationStateUseCase by lazy {
FakeLocationStateUseCase(
- fakeLocationModule, permissionsModule, localStateRepository, CityDataSource, appDesc, GlobalScope)
+ fakeLocationModule, permissionsModule, localStateRepository, CityDataSource, appDesc, context, GlobalScope
+ )
}
// ViewModelFactories
val dashBoardViewModelFactory by lazy {
- DashBoardViewModelFactory(getQuickPrivacyStateUseCase, ipScramblingStateUseCase, trackersStatisticsUseCase, trackersStateUseCase)
+ DashBoardViewModelFactory(getQuickPrivacyStateUseCase, ipScramblingStateUseCase, trackersStatisticsUseCase, trackersStateUseCase, fakeLocationStateUseCase)
}
val fakeLocationViewModelFactory by lazy {
FakeLocationViewModelFactory(
getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
- fakeLocationStateUseCase = fakeLocationStateUseCase)
+ fakeLocationStateUseCase = fakeLocationStateUseCase
+ )
}
val blockerService = BlockerInterface.getInstance(context)
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 78cb4e4..145ff32 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
@@ -44,11 +44,14 @@ class LocalStateRepository(context: Context) {
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
+ 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()
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 02fdb0f..7f288e0 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
@@ -18,6 +18,11 @@
package foundation.e.privacycentralapp.domain.usecases
import android.app.AppOpsManager
+import android.content.Context
+import android.location.Location
+import android.location.LocationListener
+import android.location.LocationManager
+import android.util.Log
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
import foundation.e.privacycentralapp.domain.entities.LocationMode
import foundation.e.privacycentralapp.dummy.CityDataSource
@@ -26,19 +31,23 @@ 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.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlin.random.Random
class FakeLocationStateUseCase(
private val fakeLocationModule: IFakeLocationModule,
-
private val permissionsModule: PermissionsPrivacyModule,
private val localStateRepository: LocalStateRepository,
private val citiesRepository: CityDataSource,
private val appDesc: ApplicationDescription,
+ private val appContext: Context,
private val coroutineScope: CoroutineScope
) {
+ private val _locationMode = MutableStateFlow(LocationMode.REAL_LOCATION)
+ val locationMode: StateFlow<LocationMode> = _locationMode
init {
coroutineScope.launch {
@@ -48,24 +57,36 @@ class FakeLocationStateUseCase(
}
}
- fun getLocationMode(): LocationMode = when(localStateRepository.fakeLocation) {
- null -> LocationMode.REAL_LOCATION
- in citiesRepository.citiesLocationsList -> LocationMode.RANDOM_LOCATION
- else -> LocationMode.SPECIFIC_LOCATION
+ private val locationManager: LocationManager
+ get() = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+
+ fun getLocationMode(): Triple<LocationMode, Float?, Float?> {
+ val fakeLocation = localStateRepository.fakeLocation
+ return if (fakeLocation != null && _locationMode.value == LocationMode.SPECIFIC_LOCATION) {
+ Triple(
+ LocationMode.SPECIFIC_LOCATION,
+ fakeLocation.first,
+ fakeLocation.second
+ )
+ } else {
+ Triple(_locationMode.value, null, null)
+ }
}
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
- // )
+ 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>?) {
@@ -74,10 +95,12 @@ class FakeLocationStateUseCase(
permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED)
}
fakeLocationModule.startFakeLocation()
-
fakeLocationModule.setFakeLocation(fakeLocation.first.toDouble(), fakeLocation.second.toDouble())
+ _locationMode.value = if (fakeLocation in citiesRepository.citiesLocationsList) LocationMode.RANDOM_LOCATION
+ else LocationMode.SPECIFIC_LOCATION
} else {
fakeLocationModule.stopFakeLocation()
+ _locationMode.value = LocationMode.REAL_LOCATION
}
}
@@ -102,4 +125,70 @@ class FakeLocationStateUseCase(
applySettings(true, null)
}
+ private var listener: LocationListener? = null
+
+ val currentLocation = MutableStateFlow<Location?>(null)
+
+ private var localListener = object : LocationListener {
+ val providerName = LocationManager.NETWORK_PROVIDER
+
+ override fun onLocationChanged(location: Location) {
+ Log.e("DebugLoc", "onLocationChanged $location")
+ currentLocation.value = location
+ }
+
+ override fun onProviderEnabled(provider: String?) {
+ Log.e("DebugLoc", "ProvuderEnabled: $provider")
+ reset(provider)
+ }
+
+ override fun onProviderDisabled(provider: String?) {
+ Log.e("DebugLoc", "ProvuderDisabled: $provider")
+ reset(provider)
+ }
+
+ private fun reset(provider: String?) {
+ if (provider == providerName) {
+ stopListeningLocation()
+ currentLocation.value = null
+ startListeningLocation()
+ }
+ }
+ }
+
+ fun startListeningLocation() {
+ requestLocationUpdates(localListener)
+ }
+
+ fun stopListeningLocation() {
+ removeUpdates(localListener)
+ }
+
+ fun requestLocationUpdates(listener: LocationListener) {
+ acquireLocationPermission()
+ try {
+ Log.e("DebugLoc", "requestLocationUpdates")
+ locationManager.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER, // TODO: tight this with fakelocation module.
+ 0L,
+ 0f,
+ listener
+ )
+ // locationManager.requestLocationUpdates(
+ // LocationManager.NETWORK_PROVIDER, // TODO: tight this with fakelocation module.
+ // 0L,
+ // 0f,
+ // listener
+ // )
+
+ val location: Location? = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
+ location?.let { listener.onLocationChanged(it) }
+ } catch (se: SecurityException) {
+ Log.e("DebugLoc", "Missing permission", se)
+ }
+ }
+
+ fun removeUpdates(listener: LocationListener) {
+ locationManager.removeUpdates(listener)
+ }
}
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 39b6138..8d5c78b 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
@@ -24,6 +24,7 @@ import foundation.e.flowmvi.SingleEventProducer
import foundation.e.flowmvi.feature.BaseFeature
import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
import foundation.e.privacycentralapp.domain.entities.LocationMode
+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
@@ -56,7 +57,7 @@ class DashboardFeature(
val totalGraph: Int? = null,
// val graphData
val trackersCount: Int? = null,
- val activeTrackersCount: Int? = null,
+ val dayTrackersCount: Int? = null,
val dayStatistics: List<Int>? = null
)
@@ -122,7 +123,8 @@ class DashboardFeature(
getPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
ipScramblingStateUseCase: IpScramblingStateUseCase,
trackersStatisticsUseCase: TrackersStatisticsUseCase,
- trackersStateUseCase: TrackersStateUseCase
+ trackersStateUseCase: TrackersStateUseCase,
+ fakeLocationStateUseCase: FakeLocationStateUseCase
): DashboardFeature =
DashboardFeature(
initialState = State(),
@@ -140,6 +142,8 @@ class DashboardFeature(
is Effect.TrackersBlockedUpdatedEffect -> state.copy(
isAllTrackersBlocked = effect.areAllTrackersBlocked
)
+ is Effect.UpdateLocationModeEffect -> state.copy(locationMode = effect.mode)
+
/*is Effect.OpenDashboardEffect -> State.DashboardState(
effect.trackersCount,
effect.activeTrackersCount,
@@ -214,6 +218,9 @@ class DashboardFeature(
},
trackersStateUseCase.areAllTrackersBlocked.map {
Effect.TrackersBlockedUpdatedEffect(it)
+ },
+ fakeLocationStateUseCase.locationMode.map {
+ Effect.UpdateLocationModeEffect(it)
}
)
/*
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
index 248e358..75525f5 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
@@ -217,7 +217,7 @@ class DashboardFragment :
binding.graph.invalidate()
}
- binding.graphLegend.text = getString(R.string.dashboard_graph_trackers_legend, state.trackersCount?.toString() ?: "No")
+ binding.graphLegend.text = getString(R.string.dashboard_graph_trackers_legend, state.dayTrackersCount?.toString() ?: "No")
if (state.dayTrackersCount != null && state.trackersCount != null) {
binding.amITracked.subTitle = getString(R.string.dashboard_am_i_tracked_subtitle, state.trackersCount, state.dayTrackersCount)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt
index 4bf01d7..0dbcdda 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt
@@ -20,6 +20,7 @@ package foundation.e.privacycentralapp.features.dashboard
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 foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase
@@ -32,7 +33,8 @@ class DashboardViewModel(
private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
private val ipScramblingStateUseCase: IpScramblingStateUseCase,
private val trackersStatisticsUseCase: TrackersStatisticsUseCase,
- private val trackersStateUseCase: TrackersStateUseCase
+ private val trackersStateUseCase: TrackersStateUseCase,
+ private val fakeLocationStateUseCase: FakeLocationStateUseCase
) : ViewModel() {
private val _actions = MutableSharedFlow<DashboardFeature.Action>()
@@ -44,7 +46,8 @@ class DashboardViewModel(
getPrivacyStateUseCase = getPrivacyStateUseCase,
ipScramblingStateUseCase = ipScramblingStateUseCase,
trackersStatisticsUseCase = trackersStatisticsUseCase,
- trackersStateUseCase = trackersStateUseCase
+ trackersStateUseCase = trackersStateUseCase,
+ fakeLocationStateUseCase = fakeLocationStateUseCase
)
}
@@ -59,9 +62,10 @@ class DashBoardViewModelFactory(
private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
private val ipScramblingStateUseCase: IpScramblingStateUseCase,
private val trackersStatisticsUseCase: TrackersStatisticsUseCase,
- private val trackersStateUseCase: TrackersStateUseCase
+ private val trackersStateUseCase: TrackersStateUseCase,
+ private val fakeLocationStateUseCase: FakeLocationStateUseCase
) : Factory<DashboardViewModel> {
override fun create(): DashboardViewModel {
- return DashboardViewModel(getPrivacyStateUseCase, ipScramblingStateUseCase, trackersStatisticsUseCase, trackersStateUseCase)
+ return DashboardViewModel(getPrivacyStateUseCase, ipScramblingStateUseCase, trackersStatisticsUseCase, trackersStateUseCase, fakeLocationStateUseCase)
}
}
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 7c6a715..b1dc938 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
@@ -17,6 +17,7 @@
package foundation.e.privacycentralapp.features.location
+import android.location.Location
import android.util.Log
import foundation.e.flowmvi.Actor
import foundation.e.flowmvi.Reducer
@@ -26,6 +27,7 @@ import foundation.e.privacycentralapp.domain.entities.LocationMode
import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -48,45 +50,54 @@ class FakeLocationFeature(
data class State(
val isEnabled: Boolean,
val mode: LocationMode,
+ val currentLocation: Location?,
val specificLatitude: Float? = null,
- val specificLongitude: Float? = null
+ val specificLongitude: Float? = null,
+ val forceRefresh: Boolean = false
)
sealed class SingleEvent {
object RandomLocationSelectedEvent : SingleEvent()
object RealLocationSelectedEvent : SingleEvent()
object SpecificLocationSavedEvent : SingleEvent()
+ data class LocationUpdatedEvent(val location: Location?) : SingleEvent()
data class ErrorEvent(val error: String) : SingleEvent()
}
sealed class Action {
object Init : Action()
+ object LeaveScreen : Action()
// Action which is triggered everytime the location is updated.
- //data class UpdateLocationAction(val latLng: LatLng) : Action()
+ // data class UpdateLocationAction(val latLng: LatLng) : Action()
object UseRealLocationAction : Action()
object UseRandomLocationAction : Action()
data class SetSpecificLocationAction(
val latitude: Float,
- val longitude: Float) : Action()
+ val longitude: Float
+ ) : Action()
}
sealed class Effect {
- data class QuickPrivacyUpdatedEffect(val isEnabled: Boolean): Effect()
+ data class QuickPrivacyUpdatedEffect(val isEnabled: Boolean) : Effect()
data class LocationModeUpdatedEffect(
val mode: LocationMode,
val latitude: Float? = null,
- val longitude: Float? = null) : Effect()
+ val longitude: Float? = null
+ ) : Effect()
+ data class LocationUpdatedEffect(val location: Location?) : Effect()
data class ErrorEffect(val message: String) : Effect()
object QuickPrivacyDisabledWarningEffect : Effect()
+ object NoEffect : Effect()
}
companion object {
fun create(
initialState: State = State(
isEnabled = false,
- mode = LocationMode.REAL_LOCATION
+ mode = LocationMode.REAL_LOCATION,
+ currentLocation = null
),
getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
fakeLocationStateUseCase: FakeLocationStateUseCase,
@@ -99,17 +110,46 @@ class FakeLocationFeature(
is Effect.LocationModeUpdatedEffect -> state.copy(
mode = effect.mode,
specificLatitude = effect.latitude,
- specificLongitude = effect.longitude)
-
- is Effect.ErrorEffect,
- Effect.QuickPrivacyDisabledWarningEffect -> state
+ specificLongitude = effect.longitude
+ )
+ // is Effect.LocationUpdatedEffect -> state.copy(currentLocation = effect.location)
+ Effect.QuickPrivacyDisabledWarningEffect -> state.copy(forceRefresh = !state.forceRefresh)
+ else -> state
}
},
actor = { state, action ->
when (action) {
is Action.Init -> merge(
getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow.map { Effect.QuickPrivacyUpdatedEffect(it) },
- flowOf(Effect.LocationModeUpdatedEffect(fakeLocationStateUseCase.getLocationMode())))
+ flow {
+ fakeLocationStateUseCase.startListeningLocation()
+ val (mode, lat, lon) = fakeLocationStateUseCase.getLocationMode()
+ emit(Effect.LocationModeUpdatedEffect(mode = mode, latitude = lat, longitude = lon))
+ },
+ fakeLocationStateUseCase.currentLocation.map { Effect.LocationUpdatedEffect(it) }
+
+ // callbackFlow {
+ // val listener = object : LocationListener {
+ // override fun onLocationChanged(location: Location) {
+ // Log.e("DebugLoc", "onLocationChanged $location")
+ // offer(Effect.LocationUpdatedEffect(location))
+ // }
+ //
+ // override fun onProviderEnabled(provider: String?) {
+ // Log.e("DebugLoc", "ProvuderEnabled: $provider")
+ // }
+ //
+ // override fun onProviderDisabled(provider: String?) {
+ // Log.e("DebugLoc", "ProvuderDisabled: $provider")
+ // }
+ // }
+ //
+ // fakeLocationStateUseCase.requestLocationUpdates(listener)
+ // // TODO: when is awaitClose called ?
+ // awaitClose { fakeLocationStateUseCase.removeUpdates(listener) }
+ // }
+
+ )
// is Action.UpdateLocationAction -> flowOf(
// Effect.LocationUpdatedEffect(
@@ -118,6 +158,10 @@ class FakeLocationFeature(
// )
// )
+ is Action.LeaveScreen -> {
+ fakeLocationStateUseCase.stopListeningLocation()
+ flowOf(Effect.NoEffect)
+ }
is Action.SetSpecificLocationAction -> {
if (state.isEnabled) {
fakeLocationStateUseCase.setSpecificLocation(
@@ -135,20 +179,22 @@ class FakeLocationFeature(
}
is Action.UseRandomLocationAction -> {
if (state.isEnabled) {
- fakeLocationStateUseCase.setRandomLocation()
- flowOf(Effect.LocationModeUpdatedEffect(LocationMode.RANDOM_LOCATION))
- } else flowOf(Effect.QuickPrivacyDisabledWarningEffect)
+ fakeLocationStateUseCase.setRandomLocation()
+ flowOf(Effect.LocationModeUpdatedEffect(LocationMode.RANDOM_LOCATION))
+ } else flowOf(Effect.QuickPrivacyDisabledWarningEffect)
}
is Action.UseRealLocationAction -> {
if (state.isEnabled) {
fakeLocationStateUseCase.stopFakeLocation()
- flowOf(Effect.LocationModeUpdatedEffect(LocationMode.REAL_LOCATION))
+ flowOf(Effect.LocationModeUpdatedEffect(LocationMode.REAL_LOCATION))
} else flowOf(Effect.QuickPrivacyDisabledWarningEffect)
}
}
},
singleEventProducer = { _, _, effect ->
when (effect) {
+ is Effect.LocationUpdatedEffect ->
+ SingleEvent.LocationUpdatedEvent(effect.location)
Effect.QuickPrivacyDisabledWarningEffect ->
SingleEvent.ErrorEvent("Enabled Quick Privacy to use functionalities")
is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
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 0f69808..ed7b9be 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
@@ -19,14 +19,11 @@ package foundation.e.privacycentralapp.features.location
import android.annotation.SuppressLint
import android.content.Context
+import android.location.Location
import android.os.Bundle
-import android.os.Looper
import android.text.Editable
import android.util.Log
-import android.view.Gravity
import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.Toast
import androidx.annotation.NonNull
@@ -39,7 +36,6 @@ import com.google.android.material.textfield.TextInputLayout
import com.google.android.material.textfield.TextInputLayout.END_ICON_CUSTOM
import com.google.android.material.textfield.TextInputLayout.END_ICON_NONE
import com.mapbox.android.core.location.LocationEngineCallback
-import com.mapbox.android.core.location.LocationEngineRequest
import com.mapbox.android.core.location.LocationEngineResult
import com.mapbox.android.core.permissions.PermissionsListener
import com.mapbox.android.core.permissions.PermissionsManager
@@ -87,25 +83,26 @@ class FakeLocationFragment :
private lateinit var binding: FragmentFakeLocationBinding
- private lateinit var mapboxMap: MapboxMap
-
+ private var mapboxMap: MapboxMap? = null
+ private var locationComponent: LocationComponent? = null
private var hoveringMarker: ImageView? = null
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(
+ mapboxMap?.locationComponent?.forceLocationUpdate(
LocationUpdate.Builder().location(it).animationDuration(100)
.build()
)
if (!isCameraMoved) {
- mapboxMap.animateCamera(
+ mapboxMap?.animateCamera(
CameraUpdateFactory.newLatLng(
LatLng(
it.latitude,
@@ -153,10 +150,11 @@ class FakeLocationFragment :
is FakeLocationFeature.SingleEvent.ErrorEvent -> {
displayToast(event.error)
}
+ is FakeLocationFeature.SingleEvent.LocationUpdatedEvent ->
+ updateLocation(event.location)
}
}
}
- lifecycleScope.launchWhenStarted { viewModel.submitAction(Action.Init) }
}
override fun onAttach(context: Context) {
@@ -180,48 +178,30 @@ class FakeLocationFragment :
mapboxMap.getUiSettings().isRotateGesturesEnabled = false
mapboxMap.setStyle(Style.MAPBOX_STREETS) { style ->
enableLocationPlugin(style)
- hoveringMarker = ImageView(requireContext())
- .apply {
- setImageResource(R.drawable.mapbox_marker_icon_default)
- val params = FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER
- )
- layoutParams = params
+
+ mapboxMap.addOnCameraMoveListener {
+ if (binding.mapView.isEnabled) {
+ mapboxMap.cameraPosition.target.let {
+ viewModel.submitAction(
+ Action.SetSpecificLocationAction(
+ it.latitude.toFloat(),
+ it.longitude.toFloat()
+ )
+ )
+ }
}
- 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) {
- // mapboxMap.cameraPosition.target.let {
- // viewModel.submitAction(
- // Action.SetSpecificLocationAction(
- // it.latitude.toFloat(),
- // it.longitude.toFloat()
- // )
- // )
- // }
- // }
-
- // }
+ }
// Bind click listeners once map is ready.
bindClickListeners()
}
}
}
- private fun getCoordinatesAfterTextChanged(inputLayout: TextInputLayout, editText: TextInputEditText, isLat: Boolean) = { editable: Editable? ->
+ private fun getCoordinatesAfterTextChanged(
+ inputLayout: TextInputLayout,
+ editText: TextInputEditText,
+ isLat: Boolean
+ ) = { editable: Editable? ->
inputJob?.cancel()
if (editable != null && editable.length > 0 && editText.isEnabled) {
inputJob = lifecycleScope.launch {
@@ -244,9 +224,16 @@ class FakeLocationFragment :
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))
+ Log.e("UpdateText", "")
+ mapboxMap?.moveCamera(
+ CameraUpdateFactory.newLatLng(
+ LatLng(lat.toDouble(), lon.toDouble())
+ )
+ )
+ // viewModel.submitAction(Action.SetSpecificLocationAction(lat, lon))
}
- } catch (e: NumberFormatException) {}
+ } catch (e: NumberFormatException) {
+ }
} catch (e: NumberFormatException) {
inputLayout.endIconMode = END_ICON_NONE
inputLayout.error = getString(R.string.location_input_error)
@@ -255,6 +242,7 @@ class FakeLocationFragment :
}
}
+ @SuppressLint("ClickableViewAccessibility")
private fun bindClickListeners() {
binding.radioUseRealLocation.setOnClickListener {
viewModel.submitAction(Action.UseRealLocationAction)
@@ -263,11 +251,23 @@ class FakeLocationFragment :
viewModel.submitAction(Action.UseRandomLocationAction)
}
binding.radioUseSpecificLocation.setOnClickListener {
- viewModel.submitAction(
-Action.SetSpecificLocationAction(displayedLocation?.first?: 0f, displayedLocation?.second?: 0f)
- )
+ mapboxMap?.cameraPosition?.target?.let {
+ viewModel.submitAction(
+ Action.SetSpecificLocationAction(it.latitude.toFloat(), it.longitude.toFloat())
+ )
+ }
}
+ // binding.mapView.addOnTouchListener { _, event ->
+ // //mapboxMap.addOnCameraMoveStartedListener {
+ // // Show marker when user starts to move across the map.
+ // if (event.action == ACTION_DOWN && binding.mapView.isEnabled) {
+ // hoveringMarker?.visibility = View.VISIBLE
+ // isCameraMoved = true
+ // }
+ // binding.mapView.onTouchEvent(event)
+ // }
+
binding.edittextLatitude.addTextChangedListener(
afterTextChanged = getCoordinatesAfterTextChanged(
binding.textlayoutLatitude,
@@ -285,10 +285,8 @@ Action.SetSpecificLocationAction(displayedLocation?.first?: 0f, displayedLocatio
)
}
+ @SuppressLint("MissingPermission")
override fun render(state: FakeLocationFeature.State) {
- hoveringMarker?.visibility = View.GONE
- isCameraMoved = false
-
binding.radioUseRandomLocation.isChecked = (state.mode == LocationMode.RANDOM_LOCATION)
binding.radioUseSpecificLocation.isChecked =
(state.mode == LocationMode.SPECIFIC_LOCATION)
@@ -296,6 +294,21 @@ Action.SetSpecificLocationAction(displayedLocation?.first?: 0f, displayedLocatio
binding.mapView.isEnabled = (state.mode == LocationMode.SPECIFIC_LOCATION)
+ if (state.mode != LocationMode.SPECIFIC_LOCATION) {
+ isCameraMoved = false
+ binding.centeredMarker.isVisible = false
+ } else {
+ binding.mapLoader.isVisible = false
+ binding.mapOverlay.isVisible = false
+ binding.centeredMarker.isVisible = true
+
+ mapboxMap?.moveCamera(
+ CameraUpdateFactory.newLatLng(
+ LatLng(state.specificLatitude?.toDouble() ?: 0.0, state.specificLongitude?.toDouble() ?: 0.0)
+ )
+ )
+ }
+
binding.textlayoutLatitude.isVisible = (state.mode == LocationMode.SPECIFIC_LOCATION)
binding.textlayoutLongitude.isVisible = (state.mode == LocationMode.SPECIFIC_LOCATION)
@@ -306,34 +319,74 @@ Action.SetSpecificLocationAction(displayedLocation?.first?: 0f, displayedLocatio
override fun actions(): Flow<Action> = viewModel.actions
@SuppressLint("MissingPermission")
- private fun enableLocationPlugin(@NonNull loadedMapStyle: Style) {
- // Check if permissions are enabled and if not request
- if (PermissionsManager.areLocationPermissionsGranted(requireContext())) {
- val locationComponent: LocationComponent = mapboxMap.locationComponent
- locationComponent.activateLocationComponent(
- LocationComponentActivationOptions.builder(
- requireContext(), loadedMapStyle
- ).useDefaultLocationEngine(true).build()
- )
- locationComponent.isLocationComponentEnabled = true
- locationComponent.cameraMode = CameraMode.TRACKING
- locationComponent.renderMode = RenderMode.NORMAL
- locationComponent.locationEngine?.let {
- it.requestLocationUpdates(
- LocationEngineRequest.Builder(DEFAULT_INTERVAL_IN_MILLISECONDS)
- .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
- .setMaxWaitTime(DEFAULT_MAX_WAIT_TIME).build(),
- locationChangeCallback,
- Looper.getMainLooper()
+ private fun updateLocation(lastLocation: Location?) {
+ lastLocation?.let { location ->
+ locationComponent?.isLocationComponentEnabled = true
+ val locationUpdate = LocationUpdate.Builder()
+ .location(location)
+ .animationDuration(100)
+ .build()
+ locationComponent?.forceLocationUpdate(locationUpdate)
+
+ // if (binding.mapView.isEnabled && !isCameraMoved) {
+ // binding.mapView.isEnabled = false
+ // mapboxMap?.moveCamera(
+ // CameraUpdateFactory.newLatLng(
+ // LatLng(
+ // location.latitude,
+ // location.longitude
+ // )
+ // )
+ // )
+ // isCameraMoved = false
+ // binding.mapView.isEnabled = true
+ if (!binding.mapView.isEnabled) {
+ binding.mapLoader.isVisible = false
+ binding.mapOverlay.isVisible = false
+ mapboxMap?.animateCamera(
+ CameraUpdateFactory.newLatLng(
+ LatLng(location.latitude, location.longitude)
+ )
)
- it.getLastLocation(locationChangeCallback)
}
- } else {
- permissionsManager = PermissionsManager(this)
- permissionsManager.requestLocationPermissions(requireActivity())
+ } ?: run {
+ locationComponent?.isLocationComponentEnabled = false
+ if (!binding.mapView.isEnabled) {
+ binding.mapLoader.isVisible = true
+ binding.mapOverlay.isVisible = true
+ }
}
}
+ @SuppressLint("MissingPermission")
+ private fun enableLocationPlugin(@NonNull loadedMapStyle: Style) {
+ // Check if permissions are enabled and if not request
+ // if (PermissionsManager.areLocationPermissionsGranted(requireContext())) {
+ locationComponent = mapboxMap?.locationComponent
+ locationComponent?.activateLocationComponent(
+ LocationComponentActivationOptions.builder(
+ requireContext(), loadedMapStyle
+ ).useDefaultLocationEngine(false).build()
+ )
+ locationComponent?.isLocationComponentEnabled = true
+ locationComponent?.cameraMode = CameraMode.NONE
+ locationComponent?.renderMode = RenderMode.NORMAL
+ // //locationComponent.locationEngine?.let {
+ // it.requestLocationUpdates(
+ // LocationEngineRequest.Builder(DEFAULT_INTERVAL_IN_MILLISECONDS)
+ // .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
+ // .setMaxWaitTime(DEFAULT_MAX_WAIT_TIME).build(),
+ // locationChangeCallback,
+ // Looper.getMainLooper()
+ // )
+ // it.getLastLocation(locationChangeCallback)
+ // }
+ // } else {
+ // permissionsManager = PermissionsManager(this)
+ // permissionsManager.requestLocationPermissions(requireActivity())
+ // }
+ }
+
override fun onStart() {
super.onStart()
binding.mapView.onStart()
@@ -341,11 +394,13 @@ Action.SetSpecificLocationAction(displayedLocation?.first?: 0f, displayedLocatio
override fun onResume() {
super.onResume()
+ viewModel.submitAction(Action.Init)
binding.mapView.onResume()
}
override fun onPause() {
super.onPause()
+ viewModel.submitAction(Action.LeaveScreen)
binding.mapView.onPause()
}
@@ -374,7 +429,7 @@ Action.SetSpecificLocationAction(displayedLocation?.first?: 0f, displayedLocatio
override fun onPermissionResult(granted: Boolean) {
if (granted) {
- val style = mapboxMap.style
+ val style = mapboxMap?.style
if (style != null) {
enableLocationPlugin(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 70ee0c1..4b91276 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
@@ -28,7 +28,8 @@ import kotlinx.coroutines.launch
class FakeLocationViewModel(
private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
- private val fakeLocationStateUseCase: FakeLocationStateUseCase) : ViewModel() {
+ private val fakeLocationStateUseCase: FakeLocationStateUseCase
+) : ViewModel() {
private val _actions = MutableSharedFlow<FakeLocationFeature.Action>()
val actions = _actions.asSharedFlow()
@@ -37,7 +38,8 @@ class FakeLocationViewModel(
FakeLocationFeature.create(
getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
fakeLocationStateUseCase = fakeLocationStateUseCase,
- coroutineScope = viewModelScope)
+ coroutineScope = viewModelScope
+ )
}
fun submitAction(action: FakeLocationFeature.Action) {
@@ -49,7 +51,8 @@ class FakeLocationViewModel(
class FakeLocationViewModelFactory(
private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
- private val fakeLocationStateUseCase: FakeLocationStateUseCase) : Factory<FakeLocationViewModel> {
+ private val fakeLocationStateUseCase: FakeLocationStateUseCase
+) : Factory<FakeLocationViewModel> {
override fun create(): FakeLocationViewModel {
return FakeLocationViewModel(getQuickPrivacyStateUseCase, fakeLocationStateUseCase)
}
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 ce7edd3..8d91425 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
@@ -29,7 +29,6 @@ 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?) {
diff --git a/app/src/main/res/layout/fragment_fake_location.xml b/app/src/main/res/layout/fragment_fake_location.xml
index 558fde0..851a7c4 100644
--- a/app/src/main/res/layout/fragment_fake_location.xml
+++ b/app/src/main/res/layout/fragment_fake_location.xml
@@ -68,13 +68,41 @@
</RadioGroup>
- <foundation.e.privacycentralapp.features.location.FakeLocationMapView
- android:id="@+id/mapView"
- android:layout_height="220dp"
+ <FrameLayout
android:layout_marginTop="16dp"
+ android:layout_height="220dp"
android:layout_width="match_parent"
- mapbox:mapbox_cameraZoom="8"
- />
+ >
+ <foundation.e.privacycentralapp.features.location.FakeLocationMapView
+ android:id="@+id/mapView"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ mapbox:mapbox_cameraZoom="8"
+ />
+ <ImageView
+ android:id="@+id/centered_marker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/mapbox_marker_icon_default"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ />
+ <View
+ android:id="@+id/map_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/grey_overlay"
+ android:visibility="visible"
+ />
+ <ProgressBar
+ android:id="@+id/map_loader"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_margin="24dp"
+ android:layout_gravity="center"
+ android:visibility="visible"
+ />
+ </FrameLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 85f0c70..f0414b6 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -16,6 +16,7 @@
<color name="grey_text">#99000000</color>
<color name="grey_text_2">#61000000</color>
<color name="grey_divider">#14212121</color>
+ <color name="grey_overlay">#66FFFFFF</color>
<color name="orange_off">#FC7222</color>
<color name="green_on">#169659</color>