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 @@ + + + + + + + + + + + + + + + + + +