From 1dd479d30787a0fdfa9e37369d90229a990c6142 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Sat, 16 Jul 2022 08:51:28 +0200 Subject: [PATCH 01/11] Update to gradle 7, update standalone flavor --- app/build.gradle | 40 ++++++++++++++---------- build.gradle | 6 ++-- gradle/wrapper/gradle-wrapper.properties | 6 ++-- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5f2b3029..52c80fbb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,10 +53,14 @@ android { minSdkVersion 30 targetSdkVersion 30 } -// google { -// applicationIdSuffix '.google' -// dimension 'os' -// } + standalone { + dimension 'os' + applicationIdSuffix '.standalone' + minSdkVersion 26 + targetSdkVersion 31 + manifestPlaceholders = [ persistent: "false" ] + + } } buildTypes { @@ -78,12 +82,11 @@ 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 - } + } + + variantFilter { variant -> + if (variant.getName().contains("debugE29") || variant.getName().contains("debugE30")) { + android.defaultConfig.signingConfig = signingConfigs.eDebug } } @@ -103,19 +106,24 @@ android { } dependencies { - - 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 - implementation 'foundation.e:privacymodule.trackerfilter:0.7.0' - implementation 'foundation.e:privacymodule.api:1.1.0' + 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' - implementation 'foundation.e:privacymodule.tor:0.2.4' + e29CompileOnly files('libs/e-ui-sdk-1.0.1-q.jar') + e29Implementation files('libs/lineage-sdk.jar') + + e30CompileOnly files('libs/e-ui-sdk-1.0.1-q.jar') + e30Implementation files('libs/lineage-sdk.jar') + + implementation 'foundation.e:privacymodule.trackerfilter:0.7.0' + implementation 'foundation.e:privacymodule.api:1.1.0' + implementation 'foundation.e:privacymodule.tor:0.2.4' // implementation Libs.Kotlin.stdlib diff --git a/build.gradle b/build.gradle index 2442f018..d8b1c57a 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,8 @@ buildscript { plugins { id 'com.diffplug.spotless' version '5.12.4' id 'com.github.ben-manes.versions' version '0.38.0' + //id 'com.android.library' version '4.1.3' apply false + id 'org.jetbrains.kotlin.android' version '1.6.10' apply false } allprojects { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dc62e2d4..35be582f 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 -- GitLab From 820655cbb6f9b7170a85a85d12924b9506453306 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Sun, 17 Jul 2022 08:15:59 +0200 Subject: [PATCH 02/11] Fix colors and launcher for standalone mode. --- app/build.gradle | 20 +++++++++---- app/src/google/res/values/strings.xml | 3 -- app/src/main/AndroidManifest.xml | 16 +++++++++-- .../privacycentralapp/DependencyContainer.kt | 5 ++-- .../standalone/res/values-night/colors.xml | 28 +++++++++++++++++++ app/src/standalone/res/values/colors.xml | 27 ++++++++++++++++++ app/src/standalone/res/values/strings.xml | 3 ++ 7 files changed, 88 insertions(+), 14 deletions(-) delete mode 100644 app/src/google/res/values/strings.xml create mode 100644 app/src/standalone/res/values-night/colors.xml create mode 100644 app/src/standalone/res/values/colors.xml create mode 100644 app/src/standalone/res/values/strings.xml diff --git a/app/build.gradle b/app/build.gradle index 52c80fbb..61e3543a 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) } @@ -58,17 +61,23 @@ android { applicationIdSuffix '.standalone' minSdkVersion 26 targetSdkVersion 31 - manifestPlaceholders = [ persistent: "false" ] - + manifestPlaceholders = [ + persistent: "false", + mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER" + ] } } buildTypes { debug { - signingConfig null // Set signing config to null as we use signingConfig per variant. + signingConfig signingConfigs.debug + // 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' @@ -121,6 +130,7 @@ dependencies { 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' implementation 'foundation.e:privacymodule.tor:0.2.4' diff --git a/app/src/google/res/values/strings.xml b/app/src/google/res/values/strings.xml deleted file mode 100644 index ebf51d06..00000000 --- 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 d285b6f8..004469b0 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 6be3724b..a44a00ac 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/standalone/res/values-night/colors.xml b/app/src/standalone/res/values-night/colors.xml new file mode 100644 index 00000000..079b968b --- /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 00000000..bd279220 --- /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 00000000..7719e7b9 --- /dev/null +++ b/app/src/standalone/res/values/strings.xml @@ -0,0 +1,3 @@ + + A-P - Standalone + -- GitLab From 90a3ec28b49a0e42d4d9523e86d5fd35041fe3e9 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Sun, 17 Jul 2022 08:16:52 +0200 Subject: [PATCH 03/11] Add fake location module in this main repository. --- .../PrivacyCentralApplication.kt | 2 +- .../usecases/FakeLocationStateUseCase.kt | 4 +- .../domain/usecases/TrackersStateUseCase.kt | 2 +- fakelocation/.gitignore | 1 + fakelocation/build.gradle | 60 +++++++++ fakelocation/consumer-rules.pro | 0 fakelocation/proguard-rules.pro | 21 ++++ fakelocation/src/main/AndroidManifest.xml | 32 +++++ .../fakelocation/FakeLocationModule.kt | 117 ++++++++++++++++++ .../fakelocation/FakeLocationService.kt | 112 +++++++++++++++++ settings.gradle | 3 +- 11 files changed, 349 insertions(+), 5 deletions(-) create mode 100644 fakelocation/.gitignore create mode 100644 fakelocation/build.gradle create mode 100644 fakelocation/consumer-rules.pro create mode 100644 fakelocation/proguard-rules.pro create mode 100644 fakelocation/src/main/AndroidManifest.xml create mode 100644 fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt create mode 100644 fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt diff --git a/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt index b23be3d0..9896d624 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 aa4276d8..c3625dac 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, 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 6417fce4..39a1d55c 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/fakelocation/.gitignore b/fakelocation/.gitignore new file mode 100644 index 00000000..42afabfd --- /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 00000000..cdfb0c32 --- /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 00000000..e69de29b diff --git a/fakelocation/proguard-rules.pro b/fakelocation/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /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 00000000..5077c24d --- /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 00000000..43a45459 --- /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 00000000..1337ddd0 --- /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/settings.gradle b/settings.gradle index e39b561b..b3bdbd6a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ include ':app' -rootProject.name = "PrivacyCentralApp" \ No newline at end of file +rootProject.name = "PrivacyCentralApp" +include ':fakelocation' -- GitLab From c1b94efb83562a9fcd299af26eb482fb999e2683 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Sun, 17 Jul 2022 20:49:04 +0200 Subject: [PATCH 04/11] Update fake location demo. Make fakeloc work in standalone mode - basic. --- .../usecases/FakeLocationStateUseCase.kt | 29 ++- .../features/location/FakeLocationFragment.kt | 34 ++- .../location/FakeLocationViewModel.kt | 16 +- build.gradle | 3 +- fakelocation/fakelocationdemo/.gitignore | 1 + fakelocation/fakelocationdemo/build.gradle | 67 ++++++ .../fakelocationdemo/proguard-rules.pro | 21 ++ .../src/main/AndroidManifest.xml | 46 ++++ .../fakelocationdemo/MainActivity.kt | 218 +++++++++++++++++ .../drawable-v24/ic_launcher_foreground.xml | 52 +++++ .../res/drawable/ic_launcher_background.xml | 221 ++++++++++++++++++ .../src/main/res/layout/activity_main.xml | 130 +++++++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 22 ++ .../mipmap-anydpi-v26/ic_launcher_round.xml | 22 ++ .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values/colors.xml | 27 +++ .../src/main/res/values/strings.xml | 20 ++ .../src/main/res/values/themes.xml | 33 +++ settings.gradle | 1 + 28 files changed, 940 insertions(+), 23 deletions(-) create mode 100644 fakelocation/fakelocationdemo/.gitignore create mode 100644 fakelocation/fakelocationdemo/build.gradle create mode 100644 fakelocation/fakelocationdemo/proguard-rules.pro create mode 100644 fakelocation/fakelocationdemo/src/main/AndroidManifest.xml create mode 100644 fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt create mode 100644 fakelocation/fakelocationdemo/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/drawable/ic_launcher_background.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 fakelocation/fakelocationdemo/src/main/res/values/colors.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/values/strings.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/values/themes.xml 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 c3625dac..cf5f2cf4 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 @@ -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/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt index 2b858e95..1b026da7 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 af20a729..afba3d0f 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/build.gradle b/build.gradle index d8b1c57a..dd8dba37 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,6 @@ buildscript { plugins { id 'com.diffplug.spotless' version '5.12.4' id 'com.github.ben-manes.versions' version '0.38.0' - //id 'com.android.library' version '4.1.3' apply false id 'org.jetbrains.kotlin.android' version '1.6.10' apply false } @@ -130,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/fakelocationdemo/.gitignore b/fakelocation/fakelocationdemo/.gitignore new file mode 100644 index 00000000..42afabfd --- /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 00000000..4ffd4c07 --- /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 00000000..481bb434 --- /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 00000000..39ab27db --- /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 00000000..55cf2cf3 --- /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 00000000..57a1725a --- /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 00000000..5eb39eec --- /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 00000000..33fce699 --- /dev/null +++ b/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + +