From eab77a885f6cca1d785ca57c4cd9182dc1a898cf Mon Sep 17 00:00:00 2001 From: Leonard Kugis Date: Sat, 23 Dec 2023 00:52:30 +0100 Subject: Implemented altitude/speed/jitter parameters --- .../data/repositories/LocalStateRepository.kt | 27 ++++++ .../domain/usecases/FakeLocationStateUseCase.kt | 29 ++++-- .../features/location/FakeLocationFragment.kt | 41 ++++++++ .../features/location/FakeLocationState.kt | 4 + .../features/location/FakeLocationViewModel.kt | 34 ++++++- app/src/main/res/layout/fragment_fake_location.xml | 103 +++++++++++++++++++++ app/src/main/res/values/strings.xml | 4 + .../domain/repositories/LocalStateRepository.kt | 4 + .../domain/usecases/FakeLocationModule.kt | 18 ++-- .../fakelocation/services/FakeLocationService.kt | 32 ++++++- 10 files changed, 269 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt index 2afd6ee..fd309a6 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt @@ -35,6 +35,9 @@ class LocalStateRepositoryImpl(context: Context) : LocalStateRepository { private const val SHARED_PREFS_FILE = "localState" private const val KEY_BLOCK_TRACKERS = "blockTrackers" private const val KEY_IP_SCRAMBLING = "ipScrambling" + private const val KEY_FAKE_ALTITUDE = "fakeAltitude" + private const val KEY_FAKE_SPEED = "fakeSpeed" + private const val KEY_FAKE_JITTER = "fakeJitter" private const val KEY_FAKE_LOCATION = "fakeLocation" private const val KEY_FAKE_LATITUDE = "fakeLatitude" private const val KEY_FAKE_LONGITUDE = "fakeLongitude" @@ -66,6 +69,30 @@ class LocalStateRepositoryImpl(context: Context) : LocalStateRepository { _fakeLocationEnabled.update { enabled } } + override var fakeAltitude: Float + get() = sharedPref.getFloat(KEY_FAKE_ALTITUDE, 3.0f) + set(value) { + sharedPref.edit() + .putFloat(KEY_FAKE_ALTITUDE, value) + .apply() + } + + override var fakeSpeed: Float + get() = sharedPref.getFloat(KEY_FAKE_SPEED, 1.0f) + set(value) { + sharedPref.edit() + .putFloat(KEY_FAKE_SPEED, value) + .apply() + } + + override var fakeJitter: Float + get() = sharedPref.getFloat(KEY_FAKE_JITTER, 3.0f) + set(value) { + sharedPref.edit() + .putFloat(KEY_FAKE_JITTER, value) + .apply() + } + override var fakeLocation: Pair get() = Pair( // Initial default value is Quezon City diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt index 282116e..8cf5f43 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt @@ -32,6 +32,7 @@ import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository import foundation.e.advancedprivacy.dummy.CityDataSource import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule +import foundation.e.advancedprivacy.features.location.FakeLocationState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -49,11 +50,11 @@ class FakeLocationStateUseCase( private val appContext: Context, coroutineScope: CoroutineScope ) { - private val _configuredLocationMode = MutableStateFlow>( - Triple(LocationMode.REAL_LOCATION, null, null) + private val _configuredLocationMode = MutableStateFlow( + FakeLocationState(LocationMode.REAL_LOCATION, null, null, null, null, false, null, null, false) ) - val configuredLocationMode: StateFlow> = _configuredLocationMode + val configuredLocationMode: StateFlow = _configuredLocationMode init { coroutineScope.launch { @@ -76,8 +77,8 @@ class FakeLocationStateUseCase( if (isEnabled && hasAcquireMockLocationPermission()) { fakeLocationModule.startFakeLocation() - fakeLocationModule.setFakeLocation(fakeLocation.first.toDouble(), fakeLocation.second.toDouble()) - localStateRepository.locationMode.value = configuredLocationMode.value.first + fakeLocationModule.setFakeLocation(localStateRepository.fakeAltitude.toDouble(), localStateRepository.fakeSpeed, localStateRepository.fakeJitter, fakeLocation.first.toDouble(), fakeLocation.second.toDouble()) + localStateRepository.locationMode.value = configuredLocationMode.value.mode } else { fakeLocationModule.stopFakeLocation() localStateRepository.locationMode.value = LocationMode.REAL_LOCATION @@ -89,6 +90,12 @@ class FakeLocationStateUseCase( permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) } + fun setFakeLocationParameters(altitude: Float, speed: Float, jitter: Float) { + localStateRepository.fakeAltitude = altitude + localStateRepository.fakeSpeed = speed + localStateRepository.fakeJitter = jitter + } + fun setSpecificLocation(latitude: Float, longitude: Float) { setFakeLocation(latitude to longitude, true) } @@ -115,16 +122,22 @@ class FakeLocationStateUseCase( isFakeLocationEnabled: Boolean, fakeLocation: Pair, isSpecificLocation: Boolean = false, - ): Triple { - return Triple( + ): FakeLocationState { + return FakeLocationState( when { !isFakeLocationEnabled -> LocationMode.REAL_LOCATION (fakeLocation in citiesRepository.citiesLocationsList && !isSpecificLocation) -> LocationMode.RANDOM_LOCATION else -> LocationMode.SPECIFIC_LOCATION }, + null, + null, + null, + null, + false, fakeLocation.first, - fakeLocation.second + fakeLocation.second, + false ) } diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt index 1c629c2..2b054ab 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt @@ -244,6 +244,31 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) } } + private fun updateMockLocationParameters() { + viewModel.submitAction( + Action.UpdateMockLocationParameters( + binding.altitude.text.toString().toFloat(), + binding.speed.text.toString().toFloat(), + binding.jitter.text.toString().toFloat(), + ) + ) + } + + @Suppress("UNUSED_PARAMETER") + private fun onAltitudeTextChanged(editable: Editable?) { + updateMockLocationParameters() + } + + @Suppress("UNUSED_PARAMETER") + private fun onSpeedTextChanged(editable: Editable?) { + updateMockLocationParameters() + } + + @Suppress("UNUSED_PARAMETER") + private fun onJitterTextChanged(editable: Editable?) { + updateMockLocationParameters() + } + @Suppress("UNUSED_PARAMETER") private fun onLatTextChanged(editable: Editable?) { if (!binding.edittextLatitude.isFocused || @@ -291,6 +316,9 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) } } + binding.altitude.addTextChangedListener(afterTextChanged = ::onAltitudeTextChanged) + binding.speed.addTextChangedListener(afterTextChanged = ::onSpeedTextChanged) + binding.jitter.addTextChangedListener(afterTextChanged = ::onJitterTextChanged) binding.edittextLatitude.addTextChangedListener(afterTextChanged = ::onLatTextChanged) binding.edittextLongitude.addTextChangedListener(afterTextChanged = ::onLonTextChanged) binding.edittextLatitude.onFocusChangeListener = latLonOnFocusChangeListener @@ -307,6 +335,19 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) binding.mapView.isEnabled = (state.mode == LocationMode.SPECIFIC_LOCATION) + binding.altitude.isEnabled = state.mode == LocationMode.SPECIFIC_LOCATION + binding.speed.isEnabled = state.mode == LocationMode.SPECIFIC_LOCATION + binding.jitter.isEnabled = state.mode == LocationMode.SPECIFIC_LOCATION + + if(!binding.altitude.isFocused) + binding.altitude.setText(state.altitude?.toString()) + + if(!binding.speed.isFocused) + binding.speed.setText(state.speed?.toString()) + + if(!binding.jitter.isFocused) + binding.jitter.setText(state.jitter?.toString()) + if (state.mode == LocationMode.REAL_LOCATION) { binding.centeredMarker.isVisible = false } else { diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationState.kt b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationState.kt index baa672b..12ebecf 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationState.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationState.kt @@ -23,6 +23,10 @@ import foundation.e.advancedprivacy.domain.entities.LocationMode data class FakeLocationState( val mode: LocationMode = LocationMode.REAL_LOCATION, val currentLocation: Location? = null, + val altitude: Float? = null, + val speed: Float? = null, + val jitter: Float? = null, + val joystick: Boolean = false, val specificLatitude: Float? = null, val specificLongitude: Float? = null, val forceRefresh: Boolean = false, diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationViewModel.kt index deca4c1..29bfbfa 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationViewModel.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationViewModel.kt @@ -52,24 +52,32 @@ class FakeLocationViewModel( val singleEvents = _singleEvents.asSharedFlow() private val specificLocationInputFlow = MutableSharedFlow() + private val mockLocationParametersInputFlow = MutableSharedFlow() @OptIn(FlowPreview::class) suspend fun doOnStartedState() = withContext(Dispatchers.Main) { launch { merge( - fakeLocationStateUseCase.configuredLocationMode.map { (mode, lat, lon) -> + fakeLocationStateUseCase.configuredLocationMode.map { ss -> _state.update { s -> s.copy( - mode = mode, - specificLatitude = lat, - specificLongitude = lon + mode = ss.mode, + altitude = ss.altitude, + speed = ss.speed, + jitter = ss.jitter, + specificLatitude = ss.specificLatitude, + specificLongitude = ss.specificLongitude ) } }, specificLocationInputFlow .debounce(SET_SPECIFIC_LOCATION_DELAY).map { action -> fakeLocationStateUseCase.setSpecificLocation(action.latitude, action.longitude) - } + }, + mockLocationParametersInputFlow + .debounce(SET_SPECIFIC_LOCATION_DELAY).map { action -> + fakeLocationStateUseCase.setFakeLocationParameters(action.altitude, action.speed, action.jitter) + }, ).collect {} } } @@ -82,6 +90,8 @@ class FakeLocationViewModel( is Action.UseRandomLocationAction -> fakeLocationStateUseCase.setRandomLocation() is Action.UseRealLocationAction -> fakeLocationStateUseCase.stopFakeLocation() + is Action.UpdateMockLocationParameters -> updateMockLocationParameters(action) + is Action.UseJoystick -> enableJoystick() } } @@ -96,6 +106,14 @@ class FakeLocationViewModel( specificLocationInputFlow.emit(action) } + private suspend fun updateMockLocationParameters(action: Action.UpdateMockLocationParameters) { + mockLocationParametersInputFlow.emit(action) + } + + private suspend fun enableJoystick() { + + } + sealed class SingleEvent { object RequestLocationPermission : SingleEvent() data class ErrorEvent(val error: String) : SingleEvent() @@ -106,6 +124,12 @@ class FakeLocationViewModel( object StopListeningLocation : Action() object UseRealLocationAction : Action() object UseRandomLocationAction : Action() + object UseJoystick : Action() + data class UpdateMockLocationParameters( + val altitude: Float, + val speed: Float, + val jitter: Float + ) : Action() data class SetSpecificLocationAction( val latitude: Float, val longitude: Float diff --git a/app/src/main/res/layout/fragment_fake_location.xml b/app/src/main/res/layout/fragment_fake_location.xml index 5da95e1..47ed7da 100644 --- a/app/src/main/res/layout/fragment_fake_location.xml +++ b/app/src/main/res/layout/fragment_fake_location.xml @@ -83,6 +83,109 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Manage my location Your location can reveal a lot about yourself or your activities.\n\nManage my location enables you to use a fake location instead of your real position. This way, your real location isn\'t shared with applications that might be snooping too much. + Altitude [m] + Speed [km/h] + Jitter [m] + Enable joystick Use my real location Use a random plausible location Use a specific location diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt index 0266f85..d1732fd 100644 --- a/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt @@ -33,6 +33,10 @@ interface LocalStateRepository { val fakeLocationEnabled: StateFlow fun setFakeLocationEnabled(enabled: Boolean) + var fakeAltitude: Float + var fakeSpeed: Float + var fakeJitter: Float + var fakeLocation: Pair val locationMode: MutableStateFlow diff --git a/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/domain/usecases/FakeLocationModule.kt b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/domain/usecases/FakeLocationModule.kt index c9aac0a..4934899 100644 --- a/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/domain/usecases/FakeLocationModule.kt +++ b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/domain/usecases/FakeLocationModule.kt @@ -85,28 +85,28 @@ class FakeLocationModule(private val context: Context) { } } - fun setFakeLocation(latitude: Double, longitude: Double) { - context.startService(FakeLocationService.buildFakeLocationIntent(context, latitude, longitude)) + fun setFakeLocation(altitude: Double, speed: Float, jitter: Float, latitude: Double, longitude: Double) { + context.startService(FakeLocationService.buildFakeLocationIntent(context, altitude, speed, jitter, latitude, longitude)) } - internal fun setTestProviderLocation(latitude: Double, longitude: Double) { + internal fun setTestProviderLocation(altitude: Double, speed: Float, jitter: Float, latitude: Double, longitude: Double) { providers.forEach { provider -> val location = Location(provider) location.latitude = latitude location.longitude = longitude // Set default value for all the other required fields. - location.altitude = 3.0 + location.altitude = altitude location.time = System.currentTimeMillis() - location.speed = 0.01f + location.speed = speed location.bearing = 1f - location.accuracy = 3f + location.accuracy = jitter location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - location.bearingAccuracyDegrees = 0.1f - location.verticalAccuracyMeters = 0.1f - location.speedAccuracyMetersPerSecond = 0.01f + location.bearingAccuracyDegrees = jitter / 10.0f + location.verticalAccuracyMeters = jitter / 10.0f + location.speedAccuracyMetersPerSecond = jitter / 100.0f } try { locationManager.setTestProviderLocation(provider, location) diff --git a/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/services/FakeLocationService.kt b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/services/FakeLocationService.kt index 6eaae54..af3b397 100644 --- a/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/services/FakeLocationService.kt +++ b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/services/FakeLocationService.kt @@ -25,6 +25,8 @@ import android.os.IBinder import android.util.Log import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule +import kotlin.random.Random + class FakeLocationService : Service() { enum class Actions { @@ -35,12 +37,18 @@ class FakeLocationService : Service() { private const val PERIOD_LOCATION_UPDATE = 1000L private const val PERIOD_UPDATES_SERIE = 2 * 60 * 1000L + private const val PARAM_ALTITUDE = "PARAM_ALTITUDE" + private const val PARAM_SPEED = "PARAM_SPEED" + private const val PARAM_JITTER = "PARAM_JITTER" private const val PARAM_LATITUDE = "PARAM_LATITUDE" private const val PARAM_LONGITUDE = "PARAM_LONGITUDE" - fun buildFakeLocationIntent(context: Context, latitude: Double, longitude: Double): Intent { + fun buildFakeLocationIntent(context: Context, altitude: Double, speed: Float, jitter: Float, latitude: Double, longitude: Double): Intent { return Intent(context, FakeLocationService::class.java).apply { action = Actions.START_FAKE_LOCATION.name + putExtra(PARAM_ALTITUDE, altitude) + putExtra(PARAM_SPEED, speed) + putExtra(PARAM_JITTER, jitter) putExtra(PARAM_LATITUDE, latitude) putExtra(PARAM_LONGITUDE, longitude) } @@ -53,6 +61,10 @@ class FakeLocationService : Service() { private var countDownTimer: CountDownTimer? = null + private var altitude: Double? = null + private var speed: Float? = null + private var jitter: Float? = null + private var fakeLocation: Pair? = null override fun onCreate() { @@ -64,7 +76,9 @@ class FakeLocationService : Service() { intent?.let { when (it.action?.let { str -> Actions.valueOf(str) }) { Actions.START_FAKE_LOCATION -> { - + altitude = it.getDoubleExtra(PARAM_ALTITUDE, 3.0) + speed = it.getFloatExtra(PARAM_ALTITUDE, 1.0f) + jitter = it.getFloatExtra(PARAM_ALTITUDE, 3.0f) fakeLocation = Pair( it.getDoubleExtra(PARAM_LATITUDE, 0.0), it.getDoubleExtra(PARAM_LONGITUDE, 0.0) @@ -87,11 +101,19 @@ class FakeLocationService : Service() { countDownTimer?.cancel() countDownTimer = object : CountDownTimer(PERIOD_UPDATES_SERIE, PERIOD_LOCATION_UPDATE) { override fun onTick(millisUntilFinished: Long) { - fakeLocation?.let { + var altitude_buf: Double = altitude ?: return + var speed_buf: Float = speed ?: return + var jitter_buf: Float = jitter ?: return + var fakeLocation_buf: Pair = fakeLocation ?: return + if(fakeLocation != null && altitude != null && speed != null && jitter != null) { + // buffer try { fakeLocationModule.setTestProviderLocation( - it.first, - it.second + altitude_buf + ((Random.nextFloat() * jitter_buf) - (jitter_buf * 0.5f)), + speed_buf + ((Random.nextFloat() * jitter_buf) - (jitter_buf * 0.5f)), + jitter_buf, + fakeLocation_buf.first + (((Random.nextFloat() * jitter_buf) - (jitter_buf * 0.5f)) / 111139.0f), + fakeLocation_buf.second + (((Random.nextFloat() * jitter_buf) - (jitter_buf * 0.5f)) / 111139.0f) ) } catch (e: Exception) { Log.d("FakeLocationService", "setting fake location", e) -- cgit v1.2.1