diff --git a/app/build.gradle b/app/build.gradle
index 5f2b3029544df83d9852c034c69497dc43d6c532..4f1b0b607b74d2e47501b92abcf1f7a0347f0573 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,7 +16,10 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- manifestPlaceholders = [ persistent: "false" ]
+ manifestPlaceholders = [
+ persistent: "false",
+ mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER"
+ ]
resValue("string", "mapbox_key", MAPBOX_KEY)
}
@@ -47,24 +50,36 @@ android {
dimension 'os'
minSdkVersion 29
targetSdkVersion 29
+ signingConfig signingConfigs.eDebug
}
e30 {
dimension 'os'
minSdkVersion 30
targetSdkVersion 30
+ signingConfig signingConfigs.eDebug
+ }
+ standalone {
+ dimension 'os'
+ applicationIdSuffix '.standalone'
+ minSdkVersion 26
+ targetSdkVersion 31
+ manifestPlaceholders = [
+ persistent: "false",
+ mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER"
+ ]
+ signingConfig signingConfigs.debug
}
-// google {
-// applicationIdSuffix '.google'
-// dimension 'os'
-// }
}
buildTypes {
debug {
- signingConfig null // Set signing config to null as we use signingConfig per variant.
+ signingConfig null // Set signing config to null as we use signingConfig per variant.
}
release {
- manifestPlaceholders = [ persistent: "true" ]
+ manifestPlaceholders = [
+ persistent: "true",
+ mainActivityIntentFilterCategory: "android.intent.category.INFO"
+ ]
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@@ -78,13 +93,6 @@ android {
variant.outputs.all { output ->
outputFileName = "Advanced_Privacy-${variant.versionName}-${variant.getFlavorName()}-${variant.buildType.name}.apk"
}
- if (variant.buildType.name == "debug") {
- if (variant.getFlavorName() == "e29" || variant.getFlavorName() == "e30") {
- variant.mergedFlavor.signingConfig = signingConfigs.eDebug
- } else {
- variant.mergedFlavor.signingConfig = signingConfigs.debug
- }
- }
}
compileOptions {
@@ -103,21 +111,23 @@ android {
}
dependencies {
+ standaloneImplementation 'foundation.e:privacymodule.standalone:1.0.0-dev'
+
+ e29Implementation 'foundation.e:privacymodule.e-29:0.4.3'
+ e30Implementation 'foundation.e:privacymodule.e-30:0.4.3'
+
+ e29CompileOnly files('libs/e-ui-sdk-1.0.1-q.jar')
+ e29Implementation files('libs/lineage-sdk.jar')
- compileOnly files('libs/e-ui-sdk-1.0.1-q.jar')
- implementation files('libs/lineage-sdk.jar')
- // include the google specific version of the modules, just for the google flavor
- //googleImplementation project(":privacymodulesgoogle")
- // include the e specific version of the modules, just for the e flavor
+ e30CompileOnly files('libs/e-ui-sdk-1.0.1-q.jar')
+ e30Implementation files('libs/lineage-sdk.jar')
+ implementation project(':fakelocation')
implementation 'foundation.e:privacymodule.trackerfilter:0.7.0'
implementation 'foundation.e:privacymodule.api:1.1.0'
- e29Implementation 'foundation.e:privacymodule.e-29:0.4.3'
- e30Implementation 'foundation.e:privacymodule.e-30:0.4.3'
implementation 'foundation.e:privacymodule.tor:0.2.4'
-
// implementation Libs.Kotlin.stdlib
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$Versions.kotlin"
// implementation Libs.AndroidX.coreKtx
diff --git a/app/src/google/res/values/strings.xml b/app/src/google/res/values/strings.xml
deleted file mode 100644
index ebf51d065e4e754c9819409f4e3ebc446cc04484..0000000000000000000000000000000000000000
--- a/app/src/google/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- google - PrivacyModulesDemo
-
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d285b6f8f473a4e765eb78e2f06d47d13c6359b5..004469b07a613b07b003020b1efebf510c1e8b6f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -33,6 +33,7 @@
>
@@ -53,19 +54,28 @@
android:resource="@xml/widget_info"
/>
-
+
+ android:launchMode="singleTask"
+ android:exported="true"
+ >
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index 6be3724b7bc61a59ab6f4ce8b0d9e5d81fb6063b..a44a00acf84db9472f020188d5cbeeec1b958abf 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -40,16 +40,15 @@ import foundation.e.privacycentralapp.features.location.FakeLocationViewModel
import foundation.e.privacycentralapp.features.trackers.TrackersViewModel
import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersFragment
import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersViewModel
+import foundation.e.privacymodules.fakelocation.FakeLocationModule
import foundation.e.privacymodules.ipscrambler.IpScramblerModule
import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
-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.privacymodules.trackers.api.BlockTrackersPrivacyModule
import foundation.e.privacymodules.trackers.api.TrackTrackersPrivacyModule
import kotlinx.coroutines.DelicateCoroutinesApi
-import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.GlobalScope
/**
@@ -61,7 +60,7 @@ class DependencyContainer(val app: Application) {
val context: Context by lazy { app.applicationContext }
// Drivers
- private val fakeLocationModule: IFakeLocationModule by lazy { FakeLocationModule(app.applicationContext) }
+ private val fakeLocationModule: FakeLocationModule by lazy { FakeLocationModule(app.applicationContext) }
private val permissionsModule by lazy { PermissionsPrivacyModule(app.applicationContext) }
private val ipScramblerModule: IIpScramblerModule by lazy { IpScramblerModule(app.applicationContext) }
diff --git a/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt
index b23be3d097e823be327fde2641fac5f6ed5db962..9896d624979f3794e2784312ffa84edbe17c06db 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt
@@ -31,6 +31,6 @@ class PrivacyCentralApplication : Application() {
super.onCreate()
Mapbox.getTelemetry()?.setUserTelemetryRequestState(false)
- dependencyContainer.initBackgroundSingletons()
+ //dependencyContainer.initBackgroundSingletons()
}
}
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 aa4276d8d3db8ce20e780f9516c4c06fddac6694..cf5f2cf4b706944f0f699cee7acbf7b7072a82a8 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
@@ -28,7 +28,7 @@ import android.util.Log
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
import foundation.e.privacycentralapp.domain.entities.LocationMode
import foundation.e.privacycentralapp.dummy.CityDataSource
-import foundation.e.privacymodules.location.IFakeLocationModule
+import foundation.e.privacymodules.fakelocation.FakeLocationModule
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.AppOpModes
import foundation.e.privacymodules.permissions.data.ApplicationDescription
@@ -39,7 +39,7 @@ import kotlinx.coroutines.launch
import kotlin.random.Random
class FakeLocationStateUseCase(
- private val fakeLocationModule: IFakeLocationModule,
+ private val fakeLocationModule: FakeLocationModule,
private val permissionsModule: PermissionsPrivacyModule,
private val localStateRepository: LocalStateRepository,
private val citiesRepository: CityDataSource,
@@ -61,23 +61,15 @@ class FakeLocationStateUseCase(
private val locationManager: LocationManager
get() = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
- private fun acquireLocationPermission() {
- if (appContext.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
- permissionsModule.toggleDangerousPermission(
- appDesc,
- android.Manifest.permission.ACCESS_FINE_LOCATION,
- true
- )
- }
+ private fun acquireLocationPermission(): Boolean {
+ return (appContext.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)
+ || false // TODO : should return true / false. permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true)
}
private fun applySettings(isQuickPrivacyEnabled: Boolean, fakeLocation: Pair?, isSpecificLocation: Boolean = false) {
_configuredLocationMode.value = computeLocationMode(fakeLocation, isSpecificLocation)
- if (isQuickPrivacyEnabled && fakeLocation != null) {
- if (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) != AppOpModes.ALLOWED) {
- permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED)
- }
+ if (isQuickPrivacyEnabled && fakeLocation != null && acquireMockLocationPermission()) {
fakeLocationModule.startFakeLocation()
fakeLocationModule.setFakeLocation(fakeLocation.first.toDouble(), fakeLocation.second.toDouble())
localStateRepository.locationMode.value = configuredLocationMode.value.first
@@ -87,6 +79,11 @@ class FakeLocationStateUseCase(
}
}
+ private fun acquireMockLocationPermission(): Boolean {
+ return (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED)
+ || permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) == null
+ }
+
fun setSpecificLocation(latitude: Float, longitude: Float) {
if (!localStateRepository.isQuickPrivacyEnabled) {
localStateRepository.setShowQuickPrivacyDisabledMessage(true)
@@ -161,8 +158,11 @@ class FakeLocationStateUseCase(
}
}
- fun startListeningLocation() {
- requestLocationUpdates(localListener)
+ fun startListeningLocation(): Boolean {
+ return if (acquireLocationPermission()) {
+ requestLocationUpdates(localListener)
+ true
+ } else false
}
fun stopListeningLocation() {
@@ -170,7 +170,6 @@ class FakeLocationStateUseCase(
}
fun requestLocationUpdates(listener: LocationListener) {
- acquireLocationPermission()
try {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, // TODO: tight this with fakelocation module.
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
index 6417fce44ddf5110d126880f01c1afa2c6a2ecc2..39a1d55c399cfb4beb769041ffc6c3ffda83e77d 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
@@ -39,7 +39,7 @@ class TrackersStateUseCase(
private val coroutineScope: CoroutineScope
) {
init {
- trackersPrivacyModule.start(trackersRepository.trackers, enableNotification = false)
+ //trackersPrivacyModule.start(trackersRepository.trackers, enableNotification = false)
coroutineScope.launch {
localStateRepository.quickPrivacyEnabledFlow.collect { enabled ->
if (enabled) {
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 2b858e9540a7fae1ccef41120c03199e173bb4ec..1b026da74ce05944d140775b28158d0865e17318 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
@@ -17,6 +17,7 @@
package foundation.e.privacycentralapp.features.location
+import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.location.Location
@@ -24,6 +25,7 @@ import android.os.Bundle
import android.text.Editable
import android.view.View
import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.NonNull
import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
@@ -81,6 +83,16 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location)
private var inputJob: Job? = null
+ private val locationPermissionRequest = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false)
+ || permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false)
+ ) {
+ viewModel.submitAction(Action.StartListeningLocation)
+ } // TODO: else.
+ }
+
companion object {
private const val DEBOUNCE_PERIOD = 1000L
}
@@ -147,6 +159,14 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location)
is FakeLocationViewModel.SingleEvent.LocationUpdatedEvent -> {
updateLocation(event.location, event.mode)
}
+ is FakeLocationViewModel.SingleEvent.RequestLocationPermission -> {
+ // TODO rationale dialog
+ //shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
+ locationPermissionRequest.launch(arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ ))
+ }
}
}
}
@@ -159,6 +179,16 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location)
}
}
+ // val locationPermissionRequest = registerForActivityResult(
+ // ActivityResultContracts.RequestMultiplePermissions()
+ // ) { permissions ->
+ // if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false)
+ // || permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false)
+ // ) {
+ // startLocationUpdates()
+ // }
+ // }
+
private fun getCoordinatesAfterTextChanged(
inputLayout: TextInputLayout,
editText: TextInputEditText,
@@ -326,13 +356,13 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location)
override fun onResume() {
super.onResume()
- viewModel.submitAction(Action.EnterScreen)
+ viewModel.submitAction(Action.StartListeningLocation)
binding.mapView.onResume()
}
override fun onPause() {
super.onPause()
- viewModel.submitAction(Action.LeaveScreen)
+ viewModel.submitAction(Action.StopListeningLocation)
binding.mapView.onPause()
}
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 af20a7298335a195988011f2300bc513ab663adb..afba3d0f6429b57eaf8b6abadae9c0ee74eabebe 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
@@ -86,8 +86,8 @@ class FakeLocationViewModel(
fun submitAction(action: Action) = viewModelScope.launch {
when (action) {
- is Action.EnterScreen -> fakeLocationStateUseCase.startListeningLocation()
- is Action.LeaveScreen -> fakeLocationStateUseCase.stopListeningLocation()
+ is Action.StartListeningLocation -> actionStartListeningLocation()
+ is Action.StopListeningLocation -> fakeLocationStateUseCase.stopListeningLocation()
is Action.SetSpecificLocationAction -> setSpecificLocation(action)
is Action.UseRandomLocationAction -> fakeLocationStateUseCase.setRandomLocation()
is Action.UseRealLocationAction ->
@@ -97,18 +97,26 @@ class FakeLocationViewModel(
}
}
+ private suspend fun actionStartListeningLocation() {
+ val started = fakeLocationStateUseCase.startListeningLocation()
+ if (!started) {
+ _singleEvents.emit(SingleEvent.RequestLocationPermission)
+ }
+ }
+
private suspend fun setSpecificLocation(action: Action.SetSpecificLocationAction) {
specificLocationInputFlow.emit(action)
}
sealed class SingleEvent {
data class LocationUpdatedEvent(val mode: LocationMode, val location: Location?) : SingleEvent()
+ object RequestLocationPermission: SingleEvent()
data class ErrorEvent(val error: String) : SingleEvent()
}
sealed class Action {
- object EnterScreen : Action()
- object LeaveScreen : Action()
+ object StartListeningLocation : Action()
+ object StopListeningLocation : Action()
object UseRealLocationAction : Action()
object UseRandomLocationAction : Action()
data class SetSpecificLocationAction(
diff --git a/app/src/standalone/res/values-night/colors.xml b/app/src/standalone/res/values-night/colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..079b968ba33a68bb7a89808dc24c60abffaf17b3
--- /dev/null
+++ b/app/src/standalone/res/values-night/colors.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ #272727
+ #5DB2FF
+
+ #CCFFFFFF
+ #8CFFFFFF
+
+ #121212
+
+
\ No newline at end of file
diff --git a/app/src/standalone/res/values/colors.xml b/app/src/standalone/res/values/colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bd2792207d83028316a6690e71f7fc6bf6f41618
--- /dev/null
+++ b/app/src/standalone/res/values/colors.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ #FFFFFF
+ #0086FF
+
+ #CC000000
+ #8C000000
+
+ #FAFAFA
+
\ No newline at end of file
diff --git a/app/src/standalone/res/values/strings.xml b/app/src/standalone/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7719e7b9a59b85e0b4951162dd050e0e2421a480
--- /dev/null
+++ b/app/src/standalone/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ A-P - Standalone
+
diff --git a/build.gradle b/build.gradle
index 2442f0188f2f2dac62086350bef63d721c83cc61..dd8dba37d0ee1095659fd3b7af56d7ebd01af955 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ buildscript {
ext.buildConfig = [
'compileSdk': 31,
'minSdk' : 26,
- 'targetSdk' : 30,
+ 'targetSdk' : 31,
'version' : [
'major': 1,
'minor': 1,
@@ -30,7 +30,7 @@ buildscript {
}
dependencies {
- classpath Libs.androidGradlePlugin
+ classpath 'com.android.tools.build:gradle:7.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
// NOTE: Do not place your application dependencies here; they belong
@@ -41,6 +41,7 @@ buildscript {
plugins {
id 'com.diffplug.spotless' version '5.12.4'
id 'com.github.ben-manes.versions' version '0.38.0'
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
allprojects {
@@ -128,7 +129,7 @@ subprojects {
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
// Treat all Kotlin warnings as errors
- allWarningsAsErrors = true
+ //allWarningsAsErrors = true
freeCompilerArgs += "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
// Set JVM target to 1.8
diff --git a/fakelocation/.gitignore b/fakelocation/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..42afabfd2abebf31384ca7797186a27a4b7dbee8
--- /dev/null
+++ b/fakelocation/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/fakelocation/build.gradle b/fakelocation/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..cdfb0c3230db7297959efd44694ce8e2f9181b3d
--- /dev/null
+++ b/fakelocation/build.gradle
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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 .
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ compileSdkVersion 32
+
+ defaultConfig {
+ minSdkVersion 26
+ targetSdkVersion 32
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$Versions.kotlin"
+
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
+
+ implementation 'androidx.core:core-ktx:1.8.0'
+ implementation 'androidx.appcompat:appcompat:1.4.2'
+ implementation 'com.google.android.material:material:1.6.1'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
diff --git a/fakelocation/consumer-rules.pro b/fakelocation/consumer-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/fakelocation/fakelocationdemo/.gitignore b/fakelocation/fakelocationdemo/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..42afabfd2abebf31384ca7797186a27a4b7dbee8
--- /dev/null
+++ b/fakelocation/fakelocationdemo/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/build.gradle b/fakelocation/fakelocationdemo/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..4ffd4c0744081fbe21feb34c718ef162e470c4e2
--- /dev/null
+++ b/fakelocation/fakelocationdemo/build.gradle
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 .
+ */
+
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ compileSdk 32
+
+ defaultConfig {
+ applicationId "foundation.e.privacymodules.fakelocationdemo"
+ minSdk 26
+ targetSdk 32
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ allWarningsAsErrors = false
+ }
+ buildFeatures {
+ dataBinding true
+ }
+}
+
+dependencies {
+ implementation project(':fakelocation')
+
+ implementation 'foundation.e:privacymodule.standalone:1.0.0-dev'
+
+ implementation 'androidx.core:core-ktx:1.8.0'
+ implementation 'androidx.appcompat:appcompat:1.4.2'
+ implementation 'com.google.android.material:material:1.6.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/proguard-rules.pro b/fakelocation/fakelocationdemo/proguard-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce
--- /dev/null
+++ b/fakelocation/fakelocationdemo/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/AndroidManifest.xml b/fakelocation/fakelocationdemo/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..39ab27db66d1b6ddd39d6d62db56d7df018a4020
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/AndroidManifest.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..55cf2cf3b9f40d64b14c5396c6169137c1ff7ebb
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 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 .
+ */
+
+package foundation.e.privacymodules.fakelocationdemo
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.location.Location
+import android.location.LocationListener
+import android.location.LocationManager
+import android.os.Bundle
+import android.os.Process.myUid
+import android.util.Log
+import android.view.View
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import foundation.e.privacymodules.fakelocation.FakeLocationModule
+import foundation.e.privacymodules.fakelocationdemo.databinding.ActivityMainBinding
+import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
+import foundation.e.privacymodules.permissions.data.AppOpModes
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+
+class MainActivity : AppCompatActivity() {
+
+ private val fakeLocationModule: FakeLocationModule by lazy { FakeLocationModule(this) }
+ private val permissionsModule by lazy { PermissionsPrivacyModule(this) }
+
+ private lateinit var binding: ActivityMainBinding
+
+ private val appDesc by lazy {
+ ApplicationDescription(
+ packageName = packageName,
+ uid = myUid(),
+ label = getString(R.string.app_name),
+ icon = null
+ )
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
+
+ getActionBar()?.setDisplayHomeAsUpEnabled(true);
+
+ binding.view = this
+ updateData("")
+ }
+
+ override fun onResume() {
+ super.onResume()
+ updateData("")
+ }
+
+
+
+ fun updateData(mockedLocation: String) {
+ binding.granted = permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED
+
+ binding.mockedLocation = mockedLocation
+ }
+
+ private val listener = object: LocationListener {
+ override fun onLocationChanged(location: Location) {
+ binding.currentLocation = "lat: ${location.latitude} - lon: ${location.longitude}"
+ }
+
+ override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onProviderEnabled(provider: String) {
+ binding.providerInfo = "onProdivderEnabled: $provider"
+ }
+
+ override fun onProviderDisabled(provider: String) {
+ binding.providerInfo = "onProdivderDisabled: $provider"
+ }
+ }
+
+ fun onClickToggleListenLocation(view: View?) {
+ //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
+// )
+
+ val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
+
+ if (binding.toggleListenLocation.isChecked) {
+ requireLocationPermissions()
+ } else {
+ locationManager.removeUpdates(listener)
+ binding.currentLocation = "no listening"
+ }
+ }
+
+ private fun startLocationUpdates() {
+ val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
+
+ try {
+ Log.e("DebugLoc", "requestLocationUpdates")
+ locationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER, // TODO: tight this with fakelocation module.
+ 1000L,
+ 1f,
+ listener
+ )
+ binding.currentLocation = "listening started"
+ } catch (se: SecurityException) {
+ Log.e("DebugLoc", "Missing permission", se)
+ }
+ }
+
+ val locationPermissionRequest = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false)
+ || permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false)
+ ) {
+ startLocationUpdates()
+ }
+ }
+
+ private fun requireLocationPermissions() {
+ if (ContextCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ startLocationUpdates()
+ } else {
+ // Before you perform the actual permission request, check whether your app
+ // already has the permissions, and whether your app needs to show a permission
+ // rationale dialog. For more details, see Request permissions.
+ locationPermissionRequest.launch(
+ arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ )
+ }
+
+ }
+
+ fun onClickPermission(view: View?) {
+ val manualAction = permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION,
+ AppOpModes.ALLOWED)
+
+ if (manualAction == null) {
+ updateData("")
+ } else {
+ //dev mode disabled
+ val alertDialog = AlertDialog.Builder(this)
+ alertDialog
+ .setTitle(manualAction.title)
+ .setMessage(manualAction.instructions)
+ .setPositiveButton("Ok") { dialog, which ->
+ startActivityForResult(manualAction.intent, 0)
+ }
+ .setNegativeButton("Cancel") { dialog, which ->
+ //user cancel
+ }
+ alertDialog.create().show()
+ }
+ }
+
+ fun onClickReset(view: View?) {
+ try {
+ fakeLocationModule.stopFakeLocation()
+ } catch(e: Exception) {
+ Log.e("FakeLoc", "Can't stop FakeLocation", e)
+ }
+ }
+
+ private fun setFakeLocation(latitude: Double, longitude: Double) {
+ try {
+ fakeLocationModule.startFakeLocation()
+ } catch(e: Exception) {
+ Log.e("FakeLoc", "Can't startFakeLocation", e)
+ }
+ fakeLocationModule.setFakeLocation(latitude, longitude)
+ updateData("lat: ${latitude} - lon: ${longitude}")
+ }
+
+ fun onClickParis(view: View?) {
+ setFakeLocation(48.8502282, 2.3542286)
+ }
+
+ fun onClickLondon(view: View?) {
+ setFakeLocation(51.5287718, -0.2416803)
+ }
+
+ fun onClickAmsterdam(view: View?) {
+ setFakeLocation(52.3547498, 4.8339211)
+ }
+}
diff --git a/fakelocation/fakelocationdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml b/fakelocation/fakelocationdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000000000000000000000000000000000000..57a1725a50d3e3f13e6861802fd22740b8e821db
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/res/drawable/ic_launcher_background.xml b/fakelocation/fakelocationdemo/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5eb39eec60b736b9ca7bed7cba9d18a9efd31e58
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml b/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000000000000000000000000000000000000..33fce699a4bc61e33b29f66650f19e19d290445c
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d154080874eb4aa15b330e4a3c6959f7f14e3406
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d154080874eb4aa15b330e4a3c6959f7f14e3406
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000000000000000000000000000000000..62b611da081676d42f6c3f78a2c91e7bcedddedb
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000000000000000000000000000000000..28d4b77f9f036a47549d47db79c16788749dca10
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000000000000000000000000000000000..9287f5083623b375139afb391af71cc533a7dd37
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000000000000000000000000000000000000..aa7d6427e6fa1074b79ccd52ef67ac15c5637e85
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000000000000000000000000000000000000..9126ae37cbc3587421d6889eadd1d91fbf1994d4
Binary files /dev/null and b/fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/fakelocation/fakelocationdemo/src/main/res/values/colors.xml b/fakelocation/fakelocationdemo/src/main/res/values/colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..29591a809c0901fa4811b600d0e5e77acd0c07b6
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/values/colors.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/res/values/strings.xml b/fakelocation/fakelocationdemo/src/main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..17b69db9ebc45090e2725a45ec01fa9a06ce89c6
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+
+
+
+ FakeLocationDemo
+
\ No newline at end of file
diff --git a/fakelocation/fakelocationdemo/src/main/res/values/themes.xml b/fakelocation/fakelocationdemo/src/main/res/values/themes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9ce6d2872267c65b3d756b5f48b41b83c90a161c
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/res/values/themes.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fakelocation/proguard-rules.pro b/fakelocation/proguard-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce
--- /dev/null
+++ b/fakelocation/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/fakelocation/src/main/AndroidManifest.xml b/fakelocation/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5077c24daf19d0d51fcfdbbcb87538db384245cd
--- /dev/null
+++ b/fakelocation/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..43a454592e787c20f14708d5e06ee4db986ea3e9
--- /dev/null
+++ b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 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 .
+ */
+
+package foundation.e.privacymodules.fakelocation
+
+import android.content.Context
+import android.content.Context.LOCATION_SERVICE
+import android.location.Criteria
+import android.location.Location
+import android.location.LocationManager
+import android.os.Build
+import android.os.SystemClock
+import android.util.Log
+
+/**
+ * Implementation of the functionality of fake location.
+ * All of them are available for normal application, so just one version is enough.
+ *
+ * @param context an Android context, to retrieve system services for example.
+ */
+class FakeLocationModule(protected val context: Context) {
+
+ /**
+ * List of all the Location provider that will be mocked.
+ */
+ private val providers = listOf(LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER)
+
+ /**
+ * Handy accessor to the locationManager service.
+ * We avoid getting it on module initialization to wait for the context to be ready.
+ */
+ private val locationManager: LocationManager get() =
+ context.getSystemService(LOCATION_SERVICE) as LocationManager
+
+ /**
+ * @see IFakeLocationModule.startFakeLocation
+ */
+ @Synchronized
+ fun startFakeLocation() {
+ providers.forEach { provider ->
+ try {
+ locationManager.removeTestProvider(provider)
+ } catch(e: Exception) {
+ Log.d("FakeLocationModule", "Test provider $provider already removed.")
+ }
+
+ locationManager.addTestProvider(
+ provider,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ true,
+ Criteria.POWER_LOW, Criteria.ACCURACY_FINE)
+ locationManager.setTestProviderEnabled(provider, true)
+ }
+ }
+
+ fun setFakeLocation(latitude: Double, longitude: Double) {
+ context.startService(FakeLocationService.buildFakeLocationIntent(context, latitude, longitude))
+ }
+
+ internal fun setTestProviderLocation(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.time = System.currentTimeMillis()
+ location.speed = 0.01f
+ location.bearing = 1f
+ location.accuracy = 3f
+ 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
+ }
+
+ locationManager.setTestProviderLocation(provider, location)
+ }
+ }
+
+ /**
+ * @see IFakeLocationModule.stopFakeLocation
+ */
+ fun stopFakeLocation() {
+ context.stopService(FakeLocationService.buildStopIntent(context))
+ providers.forEach { provider ->
+ try {
+ locationManager.setTestProviderEnabled(provider, false)
+ locationManager.removeTestProvider(provider)
+ } catch (e: Exception) {
+ Log.d("FakeLocationModule", "Test provider $provider already removed.")
+ }
+ }
+ }
+}
diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1337ddd0f741d74e0873b9dbfc32973aea91392a
--- /dev/null
+++ b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 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 .
+ */
+
+package foundation.e.privacymodules.fakelocation
+
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.os.CountDownTimer
+import android.os.IBinder
+import android.util.Log
+
+class FakeLocationService: Service() {
+
+ enum class Actions {
+ START_FAKE_LOCATION
+ }
+
+ companion object {
+ private const val PERIOD_LOCATION_UPDATE = 1000L
+ private const val PERIOD_UPDATES_SERIE = 2 * 60 * 1000L
+
+ private const val PARAM_LATITUDE = "PARAM_LATITUDE"
+ private const val PARAM_LONGITUDE = "PARAM_LONGITUDE"
+
+ fun buildFakeLocationIntent(context: Context, latitude: Double, longitude: Double): Intent {
+ return Intent(context, FakeLocationService::class.java).apply {
+ action = Actions.START_FAKE_LOCATION.name
+ putExtra(PARAM_LATITUDE, latitude)
+ putExtra(PARAM_LONGITUDE, longitude)
+ }
+ }
+
+ fun buildStopIntent(context: Context) = Intent(context, FakeLocationService::class.java)
+ }
+
+ private lateinit var fakeLocationModule: FakeLocationModule
+
+ private var countDownTimer: CountDownTimer? = null
+
+ private var fakeLocation: Pair? = null
+
+ override fun onCreate() {
+ super.onCreate()
+ fakeLocationModule = FakeLocationModule(applicationContext)
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ intent?.let {
+ when (it.action?.let { str -> Actions.valueOf(str) }) {
+ Actions.START_FAKE_LOCATION -> {
+
+ fakeLocation = Pair(
+ it.getDoubleExtra(PARAM_LATITUDE, 0.0),
+ it.getDoubleExtra(PARAM_LONGITUDE, 0.0)
+ )
+ initTimer()
+ }
+ else -> {}
+ }
+ }
+
+ return START_STICKY
+ }
+
+ override fun onDestroy() {
+ countDownTimer?.cancel()
+ super.onDestroy()
+ }
+
+
+ private fun initTimer() {
+ countDownTimer?.cancel()
+ countDownTimer = object: CountDownTimer(PERIOD_UPDATES_SERIE, PERIOD_LOCATION_UPDATE) {
+ override fun onTick(millisUntilFinished: Long) {
+ fakeLocation?.let {
+ try {
+ fakeLocationModule.setTestProviderLocation(
+ it.first,
+ it.second
+ )
+ } catch (e: Exception) {
+ Log.d("FakeLocationService", "setting fake location", e)
+ }
+ }
+ }
+
+ override fun onFinish() {
+ initTimer()
+ }
+ }.start()
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index dc62e2d4c7735b3e7ce190c94e9755aef50f4634..35be582fc9170d7b365c99f974331224dff38557 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Apr 27 19:00:30 IST 2021
+#Sat Jul 16 09:07:59 CEST 2022
distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+zipStoreBase=GRADLE_USER_HOME
diff --git a/settings.gradle b/settings.gradle
index e39b561bc2e087317561b52745c08b73d3c6aa85..b6912e3fcaf2f940e8d24772e92c44970cc097e5 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,4 @@
include ':app'
-rootProject.name = "PrivacyCentralApp"
\ No newline at end of file
+rootProject.name = "PrivacyCentralApp"
+include ':fakelocation'
+include ':fakelocation:fakelocationdemo'