summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/AndroidManifest.xml3
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt (renamed from app/src/main/java/foundation/e/privacycentralapp/MainActivity.kt)10
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/RightRadioButton.kt43
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt227
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt32
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt205
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt199
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt40
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt53
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt110
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt116
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt40
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt152
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt178
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt40
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt60
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsFragment.kt105
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsAdapter.kt63
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFeature.kt118
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFragment.kt79
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsViewModel.kt40
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt62
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/main/MainViewModel.kt22
-rw-r--r--app/src/main/res/drawable/dummy_img_map_picker.pngbin0 -> 339121 bytes
-rw-r--r--app/src/main/res/drawable/dummy_leakage_analytics.pngbin0 -> 25328 bytes
-rw-r--r--app/src/main/res/drawable/ic_apps_permissions.xml22
-rw-r--r--app/src/main/res/drawable/ic_body_monitor.xml19
-rw-r--r--app/src/main/res/drawable/ic_calendar.xml10
-rw-r--r--app/src/main/res/drawable/ic_call.xml10
-rw-r--r--app/src/main/res/drawable/ic_chevron_right_24dp.xml27
-rw-r--r--app/src/main/res/drawable/ic_facebook.xml31
-rw-r--r--app/src/main/res/drawable/ic_internet_activity.xml22
-rw-r--r--app/src/main/res/drawable/ic_location.xml10
-rw-r--r--app/src/main/res/drawable/ic_my_location.xml22
-rw-r--r--app/src/main/res/drawable/ic_privacy_toggle.xml22
-rw-r--r--app/src/main/res/drawable/ic_tracked.xml22
-rw-r--r--app/src/main/res/layout/activity_main.xml18
-rw-r--r--app/src/main/res/layout/fragment_dashboard.xml350
-rw-r--r--app/src/main/res/layout/fragment_fake_location.xml152
-rw-r--r--app/src/main/res/layout/fragment_internet_activity_policy.xml105
-rw-r--r--app/src/main/res/layout/fragment_permission_apps.xml46
-rw-r--r--app/src/main/res/layout/fragment_permissions.xml57
-rw-r--r--app/src/main/res/layout/fragment_quick_protection.xml68
-rw-r--r--app/src/main/res/layout/item_permission.xml73
-rw-r--r--app/src/main/res/layout/item_permission_apps.xml44
-rw-r--r--app/src/main/res/values-night/themes.xml16
-rw-r--r--app/src/main/res/values/strings.xml35
-rw-r--r--app/src/main/res/values/themes.xml14
48 files changed, 3143 insertions, 49 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a28e77e..2c3b055 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,13 +3,14 @@
package="foundation.e.privacycentralapp">
<application
+ android:name=".PrivacyCentralApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PrivacyCentralApp">
- <activity android:name=".MainActivity">
+ <activity android:name=".main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/app/src/main/java/foundation/e/privacycentralapp/MainActivity.kt b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt
index 3dd2145..87be346 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/MainActivity.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt
@@ -17,12 +17,6 @@
package foundation.e.privacycentralapp
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
+import android.app.Application
-class MainActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- }
-}
+class PrivacyCentralApplication : Application()
diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/RightRadioButton.kt b/app/src/main/java/foundation/e/privacycentralapp/common/RightRadioButton.kt
new file mode 100644
index 0000000..bbc108b
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/common/RightRadioButton.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.common
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.RadioButton
+
+/**
+ * A custom [RadioButton] which displays the radio drawable on the right side.
+ */
+@SuppressLint("AppCompatCustomView")
+class RightRadioButton : RadioButton {
+
+ constructor(context: Context) : super(context)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
+ context,
+ attrs,
+ defStyleAttr
+ )
+
+ // Returns layout direction as right-to-left to draw the compound button on right side.
+ override fun getLayoutDirection(): Int {
+ return LAYOUT_DIRECTION_RTL
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
new file mode 100644
index 0000000..aef994b
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.dummy
+
+import foundation.e.privacycentralapp.R
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlin.random.Random
+
+// ======================================================//
+//
+// ================ ==== ==== ===============
+// ================ ====== ====== ================
+// ==== ======== ======== ==== ====
+// ==== ========= ========= ==== ====
+// ==== ==================== ================
+// ==== ==== ======== ==== ===============
+// ==== ==== ==== ==== ====
+// ================ ==== == ==== ====
+// ================ ==== ==== ====
+//
+// ======================================================//
+
+/**
+ * This whole file acts as a dummy source. All data classes and method implementations
+ * are not proper ones and are subject to change anytime.
+ */
+
+/**
+ * Dummmy permission data class.
+ */
+data class Permission(
+ val id: Int,
+ val name: String,
+ val iconId: Int,
+ val packagesRequested: Set<String> = emptySet(),
+ val packagesAllowed: Set<String> = emptySet()
+)
+
+enum class LocationMode {
+ REAL_LOCATION, RANDOM_LOCATION, CUSTOM_LOCATION
+}
+
+enum class InternetPrivacyMode {
+ REAL_IP, HIDE_IP
+}
+
+data class Location(val mode: LocationMode, val latitude: Double, val longitude: Double)
+
+object DummyDataSource {
+
+ const val trackersCount = 77
+ private val _activeTrackersCount = MutableStateFlow(10)
+ val activeTrackersCount = _activeTrackersCount.asStateFlow()
+
+ private val _location = MutableStateFlow(Location(LocationMode.REAL_LOCATION, 0.0, 0.0))
+ val location = _location.asStateFlow()
+
+ private val _internetActivityMode = MutableStateFlow(InternetPrivacyMode.REAL_IP)
+ val internetActivityMode = _internetActivityMode.asStateFlow()
+
+ /**
+ * Declare dummy permissions with following ids
+ *
+ * [0] -> Body sensor
+ * [1] -> Calendar
+ * [2] -> Call Logs
+ * [3] -> Location
+ */
+ val permissions = arrayOf("Body Sensor", "Calendar", "Call Logs", "Location")
+
+ private val permissionIcons = arrayOf(
+ R.drawable.ic_body_monitor,
+ R.drawable.ic_calendar,
+ R.drawable.ic_call,
+ R.drawable.ic_location
+ )
+
+ val packages = arrayOf(
+ "facebook",
+ "uber",
+ "instagram",
+ "openstreetmap",
+ "truecaller",
+ "netflix",
+ "firefox",
+ "pubg",
+ "amazon",
+ "calendar",
+ "zohomail",
+ "privacycentral"
+ )
+
+ val _populatedPermissions = MutableStateFlow(fetchPermissions())
+ val populatedPermission = _populatedPermissions.asStateFlow()
+
+ private val _appsUsingLocationPerm =
+ MutableStateFlow(_populatedPermissions.value[3].packagesAllowed)
+ val appsUsingLocationPerm = _appsUsingLocationPerm.asStateFlow()
+
+ private fun fetchPermissions(): List<Permission> {
+ val result = mutableListOf<Permission>()
+ permissions.forEachIndexed { index, permission ->
+ when (index) {
+ 0 -> result.add(Permission(index, permission, permissionIcons[index]))
+ 1 -> {
+ val randomPackages = getRandomItems(packages, 8)
+ val grantedPackages = getRandomItems(randomPackages, 3)
+ result.add(
+ Permission(
+ index,
+ permission,
+ permissionIcons[index],
+ randomPackages,
+ grantedPackages
+ )
+ )
+ }
+ 2 -> {
+ val randomPackages = getRandomItems(packages, 10)
+ val grantedPackages = getRandomItems(randomPackages, 9)
+ result.add(
+ Permission(
+ index,
+ permission,
+ permissionIcons[index],
+ randomPackages,
+ grantedPackages
+ )
+ )
+ }
+ 3 -> {
+ val randomPackages = getRandomItems(packages, 5)
+ val grantedPackages = getRandomItems(randomPackages, 3)
+ result.add(
+ Permission(
+ index,
+ permission,
+ permissionIcons[index],
+ randomPackages,
+ grantedPackages
+ )
+ )
+ }
+ }
+ }
+ return result
+ }
+
+ private fun <T> getRandomItems(data: Array<T>, limit: Int): Set<T> =
+ getRandomItems(data.toSet(), limit)
+
+ private fun <T> getRandomItems(data: Set<T>, limit: Int): Set<T> {
+ val randomItems = mutableSetOf<T>()
+ val localData = data.toMutableList()
+ repeat(limit) {
+ val generated = localData.random()
+ randomItems.add(generated)
+ localData.remove(generated)
+ }
+ return randomItems
+ }
+
+ fun getPermission(permissionId: Int): Permission = populatedPermission.value[permissionId]
+
+ fun getLocationPermissionApps(): Permission = getPermission(3)
+
+ fun setLocationMode(locationMode: LocationMode, location: Location? = null): Boolean {
+ when (locationMode) {
+ LocationMode.REAL_LOCATION ->
+ _location.value =
+ Location(LocationMode.REAL_LOCATION, 24.39, 71.80)
+ LocationMode.RANDOM_LOCATION -> _location.value = randomLocation()
+ LocationMode.CUSTOM_LOCATION -> {
+ requireNotNull(location) { "Custom location should be null" }
+ _location.value = location.copy(mode = LocationMode.CUSTOM_LOCATION)
+ }
+ }
+ return true
+ }
+
+ private fun randomLocation(): Location = Location(
+ LocationMode.RANDOM_LOCATION,
+ Random.nextDouble(-90.0, 90.0),
+ Random.nextDouble(-180.0, 180.0)
+ )
+
+ fun setInternetPrivacyMode(mode: InternetPrivacyMode): Boolean {
+ _internetActivityMode.value = mode
+ return true
+ }
+
+ fun togglePermission(permissionId: Int, packageName: String, grant: Boolean) {
+ val allPermissions = _populatedPermissions.value.toMutableList()
+ val permission: Permission = allPermissions[permissionId].let { permission ->
+
+ val packagesAllowed = permission.packagesAllowed.toMutableSet()
+
+ if (grant) packagesAllowed.add(packageName)
+ else packagesAllowed.remove(packageName)
+
+ permission.copy(packagesAllowed = packagesAllowed)
+ }
+ allPermissions[permissionId] = permission
+ _populatedPermissions.value = allPermissions
+
+ // Update when permission is toggled for Location
+ if (permissionId == 3) {
+ _appsUsingLocationPerm.value = _populatedPermissions.value[permissionId].packagesAllowed
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
new file mode 100644
index 0000000..133ad84
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.dummy
+
+import android.content.Context
+import foundation.e.privacycentralapp.R
+
+fun LocationMode.mapToString(context: Context): String = when (this) {
+ LocationMode.REAL_LOCATION -> context.getString(R.string.real_location_mode)
+ LocationMode.RANDOM_LOCATION -> context.getString(R.string.random_location_mode)
+ LocationMode.CUSTOM_LOCATION -> context.getString(R.string.fake_location_mode)
+}
+
+fun InternetPrivacyMode.mapToString(context: Context): String = when (this) {
+ InternetPrivacyMode.REAL_IP -> context.getString(R.string.i_am_exposing)
+ InternetPrivacyMode.HIDE_IP -> context.getString(R.string.i_am_anonymous)
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
new file mode 100644
index 0000000..dd4f0ff
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.dashboard
+
+import android.util.Log
+import foundation.e.flowmvi.Actor
+import foundation.e.flowmvi.Reducer
+import foundation.e.flowmvi.SingleEventProducer
+import foundation.e.flowmvi.feature.BaseFeature
+import foundation.e.privacycentralapp.dummy.DummyDataSource
+import foundation.e.privacycentralapp.dummy.InternetPrivacyMode
+import foundation.e.privacycentralapp.dummy.LocationMode
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+// Define a state machine for Dashboard Feature
+class DashboardFeature(
+ initialState: State,
+ coroutineScope: CoroutineScope,
+ reducer: Reducer<State, Effect>,
+ actor: Actor<State, Action, Effect>,
+ singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent>
+) : BaseFeature<DashboardFeature.State,
+ DashboardFeature.Action,
+ DashboardFeature.Effect,
+ DashboardFeature.SingleEvent>(
+ initialState, actor, reducer, coroutineScope, { message -> Log.d("DashboardFeature", message) },
+ singleEventProducer
+) {
+ sealed class State {
+ object InitialState : State()
+ object LoadingDashboardState : State()
+ data class DashboardState(
+ val trackersCount: Int,
+ val activeTrackersCount: Int,
+ val totalApps: Int,
+ val permissionCount: Int,
+ val appsUsingLocationPerm: Int,
+ val locationMode: LocationMode,
+ val internetPrivacyMode: InternetPrivacyMode
+ ) : State()
+
+ object QuickProtectionState : State()
+ }
+
+ sealed class SingleEvent {
+ object NavigateToQuickProtectionSingleEvent : SingleEvent()
+ object NavigateToTrackersSingleEvent : SingleEvent()
+ object NavigateToInternetActivityPrivacySingleEvent : SingleEvent()
+ object NavigateToLocationSingleEvent : SingleEvent()
+ object NavigateToPermissionsSingleEvent : SingleEvent()
+ }
+
+ sealed class Action {
+ object ShowQuickPrivacyProtectionInfoAction : Action()
+ object ObserveDashboardAction : Action()
+ object ShowDashboardAction : Action()
+ object ShowFakeMyLocationAction : Action()
+ object ShowInternetActivityPrivacyAction : Action()
+ object ShowAppsPermissions : Action()
+ }
+
+ sealed class Effect {
+ object OpenQuickPrivacyProtectionEffect : Effect()
+ data class OpenDashboardEffect(
+ val trackersCount: Int,
+ val activeTrackersCount: Int,
+ val totalApps: Int,
+ val permissionCount: Int,
+ val appsUsingLocationPerm: Int,
+ val locationMode: LocationMode,
+ val internetPrivacyMode: InternetPrivacyMode
+ ) : Effect()
+
+ object LoadingDashboardEffect : Effect()
+ data class UpdateActiveTrackersCountEffect(val count: Int) : Effect()
+ data class UpdateLocationModeEffect(val mode: LocationMode) : Effect()
+ data class UpdateInternetActivityModeEffect(val mode: InternetPrivacyMode) : Effect()
+ data class UpdateAppsUsingLocationPermEffect(val apps: Int) : Effect()
+ object OpenFakeMyLocationEffect : Effect()
+ object OpenInternetActivityPrivacyEffect : Effect()
+ object OpenAppsPermissionsEffect : Effect()
+ }
+
+ companion object {
+ fun create(initialState: State, coroutineScope: CoroutineScope): DashboardFeature =
+ DashboardFeature(
+ initialState,
+ coroutineScope,
+ reducer = { state, effect ->
+ when (effect) {
+ Effect.OpenQuickPrivacyProtectionEffect -> State.QuickProtectionState
+ is Effect.OpenDashboardEffect -> State.DashboardState(
+ effect.trackersCount,
+ effect.activeTrackersCount,
+ effect.totalApps,
+ effect.permissionCount,
+ effect.appsUsingLocationPerm,
+ effect.locationMode,
+ effect.internetPrivacyMode
+ )
+ Effect.LoadingDashboardEffect -> {
+ if (state is State.InitialState) {
+ State.LoadingDashboardState
+ } else state
+ }
+ is Effect.UpdateActiveTrackersCountEffect -> {
+ if (state is State.DashboardState) {
+ state.copy(activeTrackersCount = effect.count)
+ } else state
+ }
+ is Effect.UpdateInternetActivityModeEffect -> {
+ if (state is State.DashboardState) {
+ state.copy(internetPrivacyMode = effect.mode)
+ } else state
+ }
+ is Effect.UpdateLocationModeEffect -> {
+ if (state is State.DashboardState) {
+ state.copy(locationMode = effect.mode)
+ } else state
+ }
+ is Effect.UpdateAppsUsingLocationPermEffect -> if (state is State.DashboardState) {
+ state.copy(appsUsingLocationPerm = effect.apps)
+ } else state
+
+ Effect.OpenFakeMyLocationEffect -> state
+ Effect.OpenAppsPermissionsEffect -> state
+ Effect.OpenInternetActivityPrivacyEffect -> state
+ }
+ },
+ actor = { _: State, action: Action ->
+ Log.d("Feature", "action: $action")
+ when (action) {
+ Action.ObserveDashboardAction -> merge(
+ DummyDataSource.activeTrackersCount.map {
+ Effect.UpdateActiveTrackersCountEffect(it)
+ },
+ DummyDataSource.appsUsingLocationPerm.map {
+ Effect.UpdateAppsUsingLocationPermEffect(it.size)
+ },
+ DummyDataSource.location.map {
+ Effect.UpdateLocationModeEffect(it.mode)
+ },
+ DummyDataSource.internetActivityMode.map {
+ Effect.UpdateInternetActivityModeEffect(it)
+ }
+ )
+ Action.ShowQuickPrivacyProtectionInfoAction -> flowOf(
+ Effect.OpenQuickPrivacyProtectionEffect
+ )
+ Action.ShowDashboardAction -> flow {
+ emit(Effect.LoadingDashboardEffect)
+ kotlinx.coroutines.delay(2000)
+ emit(
+ Effect.OpenDashboardEffect(
+ DummyDataSource.trackersCount,
+ DummyDataSource.activeTrackersCount.value,
+ DummyDataSource.packages.size,
+ DummyDataSource.permissions.size,
+ DummyDataSource.appsUsingLocationPerm.value.size,
+ DummyDataSource.location.value.mode,
+ DummyDataSource.internetActivityMode.value
+ )
+ )
+ }
+ Action.ShowFakeMyLocationAction -> flowOf(Effect.OpenFakeMyLocationEffect)
+ Action.ShowAppsPermissions -> flowOf(Effect.OpenAppsPermissionsEffect)
+ Action.ShowInternetActivityPrivacyAction -> flowOf(
+ Effect.OpenInternetActivityPrivacyEffect
+ )
+ }
+ },
+ singleEventProducer = { state, _, effect ->
+ Log.d("DashboardFeature", "$state, $effect")
+ if (state is State.DashboardState && effect is Effect.OpenFakeMyLocationEffect)
+ SingleEvent.NavigateToLocationSingleEvent
+ else if (state is State.QuickProtectionState && effect is Effect.OpenQuickPrivacyProtectionEffect)
+ SingleEvent.NavigateToQuickProtectionSingleEvent
+ else if (state is State.DashboardState && effect is Effect.OpenInternetActivityPrivacyEffect)
+ SingleEvent.NavigateToInternetActivityPrivacySingleEvent
+ else if (state is State.DashboardState && effect is Effect.OpenAppsPermissionsEffect)
+ SingleEvent.NavigateToPermissionsSingleEvent
+ else null
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
new file mode 100644
index 0000000..f0a7397
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.dashboard
+
+import android.graphics.Color
+import android.os.Bundle
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.style.ForegroundColorSpan
+import android.view.View
+import android.widget.ProgressBar
+import android.widget.RelativeLayout
+import android.widget.TextView
+import android.widget.Toolbar
+import androidx.core.widget.NestedScrollView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.add
+import androidx.fragment.app.commit
+import androidx.lifecycle.lifecycleScope
+import foundation.e.flowmvi.MVIView
+import foundation.e.privacycentralapp.R
+import foundation.e.privacycentralapp.dummy.mapToString
+import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyFragment
+import foundation.e.privacycentralapp.features.location.FakeLocationFragment
+import foundation.e.privacycentralapp.features.permissions.PermissionsFragment
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+
+class DashboardFragment :
+ Fragment(R.layout.fragment_dashboard),
+ MVIView<DashboardFeature.State, DashboardFeature.Action> {
+
+ private val viewModel: DashboardViewModel by activityViewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycleScope.launchWhenStarted {
+ viewModel.dashboardFeature.takeView(this, this@DashboardFragment)
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.dashboardFeature.singleEvents.collect { event ->
+ when (event) {
+ is DashboardFeature.SingleEvent.NavigateToLocationSingleEvent -> {
+ requireActivity().supportFragmentManager.commit {
+ add<FakeLocationFragment>(R.id.container)
+ setReorderingAllowed(true)
+ addToBackStack("dashboard")
+ }
+ }
+ is DashboardFeature.SingleEvent.NavigateToQuickProtectionSingleEvent -> {
+ requireActivity().supportFragmentManager.commit {
+ add<QuickProtectionFragment>(R.id.container)
+ setReorderingAllowed(true)
+ addToBackStack("dashboard")
+ }
+ }
+ is DashboardFeature.SingleEvent.NavigateToInternetActivityPrivacySingleEvent -> {
+ requireActivity().supportFragmentManager.commit {
+ add<InternetPrivacyFragment>(R.id.container)
+ setReorderingAllowed(true)
+ addToBackStack("dashboard")
+ }
+ }
+ is DashboardFeature.SingleEvent.NavigateToPermissionsSingleEvent -> {
+ requireActivity().supportFragmentManager.commit {
+ add<PermissionsFragment>(R.id.container)
+ setReorderingAllowed(true)
+ addToBackStack("dashboard")
+ }
+ }
+ DashboardFeature.SingleEvent.NavigateToTrackersSingleEvent -> {
+ }
+ }
+ }
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction)
+ viewModel.submitAction(DashboardFeature.Action.ObserveDashboardAction)
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
+ setupToolbar(toolbar)
+ addClickToMore(view.findViewById(R.id.personal_leakag_info))
+ view.let {
+ it.findViewById<TextView>(R.id.tap_to_enable_quick_protection).setOnClickListener {
+ viewModel.submitAction(DashboardFeature.Action.ShowQuickPrivacyProtectionInfoAction)
+ }
+ it.findViewById<RelativeLayout>(R.id.my_location).setOnClickListener {
+ viewModel.submitAction(DashboardFeature.Action.ShowFakeMyLocationAction)
+ }
+ it.findViewById<RelativeLayout>(R.id.internet_activity_privacy).setOnClickListener {
+ viewModel.submitAction(DashboardFeature.Action.ShowInternetActivityPrivacyAction)
+ }
+ it.findViewById<RelativeLayout>(R.id.apps_permissions).setOnClickListener {
+ viewModel.submitAction(DashboardFeature.Action.ShowAppsPermissions)
+ }
+ }
+ }
+
+ private fun setupToolbar(toolbar: Toolbar) {
+ val activity = requireActivity()
+ activity.setActionBar(toolbar)
+ activity.title = "My Privacy Dashboard"
+ }
+
+ private fun addClickToMore(textView: TextView) {
+ val clickToMore = SpannableString("Click to learn more")
+ clickToMore.setSpan(
+ ForegroundColorSpan(Color.parseColor("#007fff")),
+ 0,
+ clickToMore.length,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ textView.append(clickToMore)
+ }
+
+ override fun render(state: DashboardFeature.State) {
+ when (state) {
+ is DashboardFeature.State.InitialState, is DashboardFeature.State.LoadingDashboardState -> {
+ view?.let {
+ it.findViewById<ProgressBar>(R.id.loadingSpinner).visibility = View.VISIBLE
+ it.findViewById<NestedScrollView>(R.id.scrollContainer).visibility = View.GONE
+ }
+ }
+ is DashboardFeature.State.DashboardState -> {
+ view?.let { view ->
+ view.findViewById<ProgressBar>(R.id.loadingSpinner).visibility = View.GONE
+ view.findViewById<NestedScrollView>(R.id.scrollContainer).visibility =
+ View.VISIBLE
+ view.findViewById<TextView>(R.id.am_i_tracked_subtitle).text = getString(
+ R.string.am_i_tracked_subtitle,
+ state.trackersCount,
+ state.activeTrackersCount
+ )
+ view.findViewById<TextView>(R.id.apps_permissions_subtitle).text = getString(
+ R.string.apps_permissions_subtitle,
+ state.totalApps,
+ state.permissionCount
+ )
+ view.findViewById<TextView>(R.id.my_location_subtitle).let { textView ->
+ textView.text = getString(
+ R.string.my_location_subtitle,
+ state.appsUsingLocationPerm,
+ )
+ textView.append(
+ SpannableString(state.locationMode.mapToString(requireContext()))
+ .also {
+ it.setSpan(
+ ForegroundColorSpan(Color.parseColor("#007fff")),
+ 0,
+ it.length,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ }
+ )
+ }
+ view.findViewById<TextView>(R.id.internet_activity_privacy_subtitle)
+ .let { textView ->
+ textView.text = getString(R.string.internet_activity_privacy_subtitle)
+ textView.append(
+ SpannableString(state.internetPrivacyMode.mapToString(requireContext()))
+ .also {
+ it.setSpan(
+ ForegroundColorSpan(Color.parseColor("#007fff")),
+ 0,
+ it.length,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ }
+ )
+ }
+ }
+ }
+ DashboardFeature.State.QuickProtectionState -> {
+ }
+ }
+ }
+
+ override fun actions(): Flow<DashboardFeature.Action> = viewModel.actions
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt
new file mode 100644
index 0000000..1a3b3df
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.dashboard
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+class DashboardViewModel : ViewModel() {
+
+ private val _actions = MutableSharedFlow<DashboardFeature.Action>()
+ val actions = _actions.asSharedFlow()
+
+ val dashboardFeature: DashboardFeature by lazy {
+ DashboardFeature.create(DashboardFeature.State.InitialState, coroutineScope = viewModelScope)
+ }
+
+ fun submitAction(action: DashboardFeature.Action) {
+ viewModelScope.launch {
+ _actions.emit(action)
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt
new file mode 100644
index 0000000..c120b49
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.dashboard
+
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import android.widget.Toolbar
+import androidx.activity.addCallback
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import foundation.e.privacycentralapp.R
+
+class QuickProtectionFragment : Fragment(R.layout.fragment_quick_protection) {
+
+ private val viewModel: DashboardViewModel by activityViewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
+ setupToolbar(toolbar)
+ }
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ requireActivity().onBackPressedDispatcher.addCallback(this, true) {
+ viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction)
+ this.isEnabled = false
+ requireActivity().onBackPressed()
+ }
+ }
+
+ private fun setupToolbar(toolbar: Toolbar) {
+ val activity = requireActivity()
+ activity.setActionBar(toolbar)
+ activity.title = "Quick protection"
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt
new file mode 100644
index 0000000..b34024e
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.internetprivacy
+
+import android.util.Log
+import foundation.e.flowmvi.Actor
+import foundation.e.flowmvi.Reducer
+import foundation.e.flowmvi.SingleEventProducer
+import foundation.e.flowmvi.feature.BaseFeature
+import foundation.e.privacycentralapp.dummy.DummyDataSource
+import foundation.e.privacycentralapp.dummy.InternetPrivacyMode
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+
+// Define a state machine for Internet privacy feature
+class InternetPrivacyFeature(
+ initialState: State,
+ coroutineScope: CoroutineScope,
+ reducer: Reducer<State, Effect>,
+ actor: Actor<State, Action, Effect>,
+ singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent>
+) : BaseFeature<InternetPrivacyFeature.State, InternetPrivacyFeature.Action, InternetPrivacyFeature.Effect, InternetPrivacyFeature.SingleEvent>(
+ initialState,
+ actor,
+ reducer,
+ coroutineScope,
+ { message -> Log.d("InternetPrivacyFeature", message) },
+ singleEventProducer
+) {
+ data class State(val mode: InternetPrivacyMode)
+
+ sealed class SingleEvent {
+ object RealIPSelectedEvent : SingleEvent()
+ object HiddenIPSelectedEvent : SingleEvent()
+ data class ErrorEvent(val error: String) : SingleEvent()
+ }
+
+ sealed class Action {
+ object LoadInternetModeAction : Action()
+ object UseRealIPAction : Action()
+ object UseHiddenIPAction : Action()
+ }
+
+ sealed class Effect {
+ data class ModeUpdatedEffect(val mode: InternetPrivacyMode) : Effect()
+ data class ErrorEffect(val message: String) : Effect()
+ }
+
+ companion object {
+ fun create(
+ initialState: State = State(InternetPrivacyMode.REAL_IP),
+ coroutineScope: CoroutineScope
+ ) = InternetPrivacyFeature(
+ initialState, coroutineScope,
+ reducer = { state, effect ->
+ when (effect) {
+ is Effect.ModeUpdatedEffect -> state.copy(mode = effect.mode)
+ is Effect.ErrorEffect -> state
+ }
+ },
+ actor = { _, action ->
+ when (action) {
+ Action.LoadInternetModeAction -> flowOf(Effect.ModeUpdatedEffect(DummyDataSource.internetActivityMode.value))
+ Action.UseHiddenIPAction, Action.UseRealIPAction -> flow {
+ val success =
+ DummyDataSource.setInternetPrivacyMode(if (action is Action.UseHiddenIPAction) InternetPrivacyMode.HIDE_IP else InternetPrivacyMode.REAL_IP)
+ emit(
+ if (success) Effect.ModeUpdatedEffect(DummyDataSource.internetActivityMode.value) else Effect.ErrorEffect(
+ "Couldn't update internet mode"
+ )
+ )
+ }
+ }
+ },
+ singleEventProducer = { _, action, effect ->
+ when (action) {
+ Action.UseRealIPAction, Action.UseHiddenIPAction -> when (effect) {
+ is Effect.ModeUpdatedEffect -> {
+ if (effect.mode == InternetPrivacyMode.REAL_IP) {
+ SingleEvent.RealIPSelectedEvent
+ } else {
+ SingleEvent.HiddenIPSelectedEvent
+ }
+ }
+ is Effect.ErrorEffect -> {
+ SingleEvent.ErrorEvent(effect.message)
+ }
+ }
+ else -> null
+ }
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt
new file mode 100644
index 0000000..a8c1671
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.internetprivacy
+
+import android.os.Bundle
+import android.view.View
+import android.widget.RadioButton
+import android.widget.Toast
+import android.widget.Toolbar
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import foundation.e.flowmvi.MVIView
+import foundation.e.privacycentralapp.R
+import foundation.e.privacycentralapp.dummy.InternetPrivacyMode
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+
+class InternetPrivacyFragment :
+ Fragment(R.layout.fragment_internet_activity_policy),
+ MVIView<InternetPrivacyFeature.State, InternetPrivacyFeature.Action> {
+
+ private val viewModel: InternetPrivacyViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycleScope.launchWhenStarted {
+ viewModel.internetPrivacyFeature.takeView(this, this@InternetPrivacyFragment)
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.internetPrivacyFeature.singleEvents.collect { event ->
+ when (event) {
+ is InternetPrivacyFeature.SingleEvent.ErrorEvent -> displayToast(event.error)
+ InternetPrivacyFeature.SingleEvent.HiddenIPSelectedEvent -> displayToast("Your IP is hidden")
+ InternetPrivacyFeature.SingleEvent.RealIPSelectedEvent -> displayToast("Your IP is visible to internet")
+ }
+ }
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(InternetPrivacyFeature.Action.LoadInternetModeAction)
+ }
+ }
+
+ private fun displayToast(message: String) {
+ Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
+ .show()
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
+ setupToolbar(toolbar)
+ bindClickListeners(view)
+ }
+
+ private fun setupToolbar(toolbar: Toolbar) {
+ val activity = requireActivity()
+ activity.setActionBar(toolbar)
+ activity.title = "My Internet Activity Privacy"
+ }
+
+ private fun bindClickListeners(fragmentView: View) {
+ fragmentView.let {
+ it.findViewById<RadioButton>(R.id.radio_use_real_ip)
+ .setOnClickListener { radioButton ->
+ toggleIP(radioButton)
+ }
+ it.findViewById<RadioButton>(R.id.radio_use_hidden_ip)
+ .setOnClickListener { radioButton ->
+ toggleIP(radioButton)
+ }
+ }
+ }
+
+ private fun toggleIP(radioButton: View?) {
+ if (radioButton is RadioButton) {
+ val checked = radioButton.isChecked
+ when (radioButton.id) {
+ R.id.radio_use_real_ip ->
+ if (checked) {
+ viewModel.submitAction(InternetPrivacyFeature.Action.UseRealIPAction)
+ }
+ R.id.radio_use_hidden_ip ->
+ if (checked) {
+ viewModel.submitAction(InternetPrivacyFeature.Action.UseHiddenIPAction)
+ }
+ }
+ }
+ }
+
+ override fun render(state: InternetPrivacyFeature.State) {
+ view?.let {
+ it.findViewById<RadioButton>(R.id.radio_use_hidden_ip).isChecked =
+ state.mode == InternetPrivacyMode.HIDE_IP
+ it.findViewById<RadioButton>(R.id.radio_use_real_ip).isChecked =
+ state.mode == InternetPrivacyMode.REAL_IP
+ }
+ }
+
+ override fun actions(): Flow<InternetPrivacyFeature.Action> = viewModel.actions
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt
new file mode 100644
index 0000000..b66b611
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.internetprivacy
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+class InternetPrivacyViewModel : ViewModel() {
+
+ private val _actions = MutableSharedFlow<InternetPrivacyFeature.Action>()
+ val actions = _actions.asSharedFlow()
+
+ val internetPrivacyFeature: InternetPrivacyFeature by lazy {
+ InternetPrivacyFeature.create(coroutineScope = viewModelScope)
+ }
+
+ fun submitAction(action: InternetPrivacyFeature.Action) {
+ viewModelScope.launch {
+ _actions.emit(action)
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt
new file mode 100644
index 0000000..6b00490
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.location
+
+import android.util.Log
+import foundation.e.flowmvi.Actor
+import foundation.e.flowmvi.Reducer
+import foundation.e.flowmvi.SingleEventProducer
+import foundation.e.flowmvi.feature.BaseFeature
+import foundation.e.privacycentralapp.dummy.DummyDataSource
+import foundation.e.privacycentralapp.dummy.Location
+import foundation.e.privacycentralapp.dummy.LocationMode
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+// Define a state machine for Fake location feature
+class FakeLocationFeature(
+ initialState: State,
+ coroutineScope: CoroutineScope,
+ reducer: Reducer<State, Effect>,
+ actor: Actor<State, Action, Effect>,
+ singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent>
+) : BaseFeature<FakeLocationFeature.State, FakeLocationFeature.Action, FakeLocationFeature.Effect, FakeLocationFeature.SingleEvent>(
+ initialState, actor, reducer, coroutineScope, { message -> Log.d("FakeLocationFeature", message) },
+ singleEventProducer
+) {
+ sealed class State {
+ object InitialState : State()
+ data class LocationState(val location: Location) : State()
+ }
+
+ sealed class SingleEvent {
+ object RandomLocationSelectedEvent : SingleEvent()
+ object RealLocationSelectedEvent : SingleEvent()
+ object SpecificLocationSavedEvent : SingleEvent()
+ data class ErrorEvent(val error: String) : SingleEvent()
+ }
+
+ sealed class Action {
+ object ObserveLocationAction : Action()
+ object UseRealLocationAction : Action()
+ object UseRandomLocationAction : Action()
+ object UseSpecificLocationAction : Action()
+ data class AddSpecificLocationAction(val latitude: Double, val longitude: Double) : Action()
+ }
+
+ sealed class Effect {
+ data class LocationUpdatedEffect(val location: Location) : Effect()
+ object RealLocationSelectedEffect : Effect()
+ object RandomLocationSelectedEffect : Effect()
+ data class SpecificLocationSelectedEffect(val location: Location) : Effect()
+ object SpecificLocationSavedEffect : Effect()
+ data class ErrorEffect(val message: String) : Effect()
+ }
+
+ companion object {
+ fun create(
+ initialState: State = State.InitialState,
+ coroutineScope: CoroutineScope
+ ) = FakeLocationFeature(
+ initialState, coroutineScope,
+ reducer = { state, effect ->
+ when (effect) {
+ Effect.RandomLocationSelectedEffect,
+ Effect.RealLocationSelectedEffect, is Effect.ErrorEffect, Effect.SpecificLocationSavedEffect -> state
+ is Effect.LocationUpdatedEffect -> State.LocationState(effect.location)
+ is Effect.SpecificLocationSelectedEffect -> State.LocationState(effect.location)
+ }
+ },
+ actor = { _, action ->
+ when (action) {
+ is Action.ObserveLocationAction -> DummyDataSource.location.map {
+ Effect.LocationUpdatedEffect(it)
+ }
+ is Action.AddSpecificLocationAction -> {
+ val location = Location(
+ LocationMode.CUSTOM_LOCATION,
+ action.latitude,
+ action.longitude
+ )
+ val success = DummyDataSource.setLocationMode(
+ LocationMode.CUSTOM_LOCATION,
+ location
+ )
+ if (success) {
+ flowOf(
+ Effect.SpecificLocationSavedEffect
+ )
+ } else {
+ flowOf(
+ Effect.ErrorEffect("Couldn't select location")
+ )
+ }
+ }
+ Action.UseRandomLocationAction -> {
+ val success = DummyDataSource.setLocationMode(LocationMode.RANDOM_LOCATION)
+ if (success) {
+ flowOf(
+ Effect.RandomLocationSelectedEffect
+ )
+ } else {
+ flowOf(
+ Effect.ErrorEffect("Couldn't select location")
+ )
+ }
+ }
+ Action.UseRealLocationAction -> {
+ val success = DummyDataSource.setLocationMode(LocationMode.REAL_LOCATION)
+ if (success) {
+ flowOf(
+ Effect.RealLocationSelectedEffect
+ )
+ } else {
+ flowOf(
+ Effect.ErrorEffect("Couldn't select location")
+ )
+ }
+ }
+ Action.UseSpecificLocationAction -> {
+ val location = DummyDataSource.location.value
+ flowOf(Effect.SpecificLocationSelectedEffect(location.copy(mode = LocationMode.CUSTOM_LOCATION)))
+ }
+ }
+ },
+ singleEventProducer = { _, _, effect ->
+ when (effect) {
+ Effect.RandomLocationSelectedEffect -> SingleEvent.RandomLocationSelectedEvent
+ Effect.SpecificLocationSavedEffect -> SingleEvent.SpecificLocationSavedEvent
+ Effect.RealLocationSelectedEffect -> SingleEvent.RealLocationSelectedEvent
+ is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
+ else -> null
+ }
+ }
+ )
+ }
+}
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
new file mode 100644
index 0000000..5b58293
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.location
+
+import android.os.Bundle
+import android.text.Editable
+import android.util.Log
+import android.view.View
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.RadioButton
+import android.widget.Toast
+import android.widget.Toolbar
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import com.google.android.material.textfield.TextInputLayout
+import foundation.e.flowmvi.MVIView
+import foundation.e.privacycentralapp.R
+import foundation.e.privacycentralapp.dummy.LocationMode
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+
+class FakeLocationFragment :
+ Fragment(R.layout.fragment_fake_location),
+ MVIView<FakeLocationFeature.State, FakeLocationFeature.Action> {
+
+ private val viewModel: FakeLocationViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycleScope.launchWhenStarted {
+ viewModel.fakeLocationFeature.takeView(this, this@FakeLocationFragment)
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.fakeLocationFeature.singleEvents.collect { event ->
+ when (event) {
+ is FakeLocationFeature.SingleEvent.RandomLocationSelectedEvent -> displayToast("Random location selected")
+ is FakeLocationFeature.SingleEvent.SpecificLocationSavedEvent -> displayToast("Specific location selected")
+ is FakeLocationFeature.SingleEvent.ErrorEvent -> displayToast(event.error)
+ FakeLocationFeature.SingleEvent.RealLocationSelectedEvent -> displayToast("Real location selected")
+ }
+ }
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(FakeLocationFeature.Action.ObserveLocationAction)
+ }
+ }
+
+ private fun displayToast(message: String) {
+ Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
+ .show()
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
+ setupToolbar(toolbar)
+ bindClickListeners(view)
+ }
+
+ private fun bindClickListeners(fragmentView: View) {
+ fragmentView.let {
+ it.findViewById<RadioButton>(R.id.radio_use_real_location)
+ .setOnClickListener { radioButton ->
+ toggleLocationType(radioButton)
+ }
+ it.findViewById<RadioButton>(R.id.radio_use_random_location)
+ .setOnClickListener { radioButton ->
+ toggleLocationType(radioButton)
+ }
+ it.findViewById<RadioButton>(R.id.radio_use_specific_location)
+ .setOnClickListener { radioButton ->
+ toggleLocationType(radioButton)
+ }
+ it.findViewById<Button>(R.id.button_add_location)
+ .setOnClickListener {
+ val latitude =
+ fragmentView.findViewById<TextInputLayout>(R.id.edittext_latitude).editText?.text.toString()
+ .toDouble()
+ val longitude =
+ fragmentView.findViewById<TextInputLayout>(R.id.edittext_longitude).editText?.text.toString()
+ .toDouble()
+ saveSpecificLocation(latitude, longitude)
+ }
+ }
+ }
+
+ private fun saveSpecificLocation(latitude: Double, longitude: Double) {
+ viewModel.submitAction(
+ FakeLocationFeature.Action.AddSpecificLocationAction(latitude, longitude)
+ )
+ }
+
+ private fun toggleLocationType(radioButton: View?) {
+ if (radioButton is RadioButton) {
+ val checked = radioButton.isChecked
+ when (radioButton.id) {
+ R.id.radio_use_real_location ->
+ if (checked) {
+ viewModel.submitAction(FakeLocationFeature.Action.UseRealLocationAction)
+ }
+ R.id.radio_use_random_location ->
+ if (checked) {
+ viewModel.submitAction(FakeLocationFeature.Action.UseRandomLocationAction)
+ }
+ R.id.radio_use_specific_location ->
+ if (checked) {
+ viewModel.submitAction(FakeLocationFeature.Action.UseSpecificLocationAction)
+ }
+ }
+ }
+ }
+
+ private fun setupToolbar(toolbar: Toolbar) {
+ val activity = requireActivity()
+ activity.setActionBar(toolbar)
+ activity.title = "Fake My Location"
+ }
+
+ override fun render(state: FakeLocationFeature.State) {
+ when (state) {
+ is FakeLocationFeature.State.LocationState -> {
+ Log.d("FakeMyLocation", "State: $state")
+ when (state.location.mode) {
+ LocationMode.REAL_LOCATION, LocationMode.RANDOM_LOCATION ->
+ view?.let {
+ it.findViewById<RadioButton>(R.id.radio_use_random_location).isChecked =
+ (state.location.mode == LocationMode.RANDOM_LOCATION)
+ it.findViewById<RadioButton>(R.id.radio_use_real_location).isChecked =
+ (state.location.mode == LocationMode.REAL_LOCATION)
+ it.findViewById<ImageView>(R.id.dummy_img_map).visibility = View.GONE
+ it.findViewById<TextInputLayout>(R.id.edittext_latitude).visibility =
+ View.GONE
+ it.findViewById<TextInputLayout>(R.id.edittext_longitude).visibility =
+ View.GONE
+ it.findViewById<Button>(R.id.button_add_location).visibility = View.GONE
+ }
+ LocationMode.CUSTOM_LOCATION -> view?.let {
+ it.findViewById<RadioButton>(R.id.radio_use_specific_location).isChecked =
+ true
+ it.findViewById<ImageView>(R.id.dummy_img_map).visibility = View.VISIBLE
+ it.findViewById<TextInputLayout>(R.id.edittext_latitude).apply {
+ visibility = View.VISIBLE
+ editText?.text = Editable.Factory.getInstance()
+ .newEditable(state.location.latitude.toString())
+ }
+ it.findViewById<TextInputLayout>(R.id.edittext_longitude).apply {
+ visibility = View.VISIBLE
+ editText?.text = Editable.Factory.getInstance()
+ .newEditable(state.location.longitude.toString())
+ }
+ it.findViewById<Button>(R.id.button_add_location).visibility = View.VISIBLE
+ }
+ }
+ }
+ FakeLocationFeature.State.InitialState -> {
+ }
+ }
+ }
+
+ override fun actions(): Flow<FakeLocationFeature.Action> = viewModel.actions
+}
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
new file mode 100644
index 0000000..eb55fba
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.location
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+class FakeLocationViewModel : ViewModel() {
+
+ private val _actions = MutableSharedFlow<FakeLocationFeature.Action>()
+ val actions = _actions.asSharedFlow()
+
+ val fakeLocationFeature: FakeLocationFeature by lazy {
+ FakeLocationFeature.create(coroutineScope = viewModelScope)
+ }
+
+ fun submitAction(action: FakeLocationFeature.Action) {
+ viewModelScope.launch {
+ _actions.emit(action)
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt
new file mode 100644
index 0000000..4f9b602
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.permissions
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Switch
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import foundation.e.privacycentralapp.R
+
+class PermissionAppsAdapter(
+ private val dataSet: List<Pair<String, Boolean>>,
+ private val listener: (String, Boolean) -> Unit
+) :
+ RecyclerView.Adapter<PermissionAppsAdapter.PermissionViewHolder>() {
+
+ class PermissionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val appName: TextView = view.findViewById(R.id.app_title)
+
+ @SuppressLint("UseSwitchCompatOrMaterialCode")
+ val togglePermission: Switch = view.findViewById(R.id.togglePermission)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PermissionViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_permission_apps, parent, false)
+ val holder = PermissionViewHolder(view)
+ holder.togglePermission.setOnCheckedChangeListener { _, isChecked ->
+ listener(dataSet[holder.adapterPosition].first, isChecked)
+ }
+ view.findViewById<Switch>(R.id.togglePermission)
+ return holder
+ }
+
+ override fun onBindViewHolder(holder: PermissionViewHolder, position: Int) {
+ val permission = dataSet[position]
+ holder.appName.text = permission.first
+ holder.togglePermission.isChecked = permission.second
+ }
+
+ override fun getItemCount(): Int = dataSet.size
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsFragment.kt
new file mode 100644
index 0000000..224d1be
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsFragment.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.permissions
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import android.widget.Toast
+import android.widget.Toolbar
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import foundation.e.flowmvi.MVIView
+import foundation.e.privacycentralapp.R
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+
+class PermissionAppsFragment :
+ Fragment(R.layout.fragment_permission_apps),
+ MVIView<PermissionsFeature.State, PermissionsFeature.Action> {
+
+ private val viewModel: PermissionsViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycleScope.launchWhenStarted {
+ viewModel.permissionsFeature.takeView(this, this@PermissionAppsFragment)
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.permissionsFeature.singleEvents.collect { event ->
+ when (event) {
+ is PermissionsFeature.SingleEvent.ErrorEvent -> displayToast(event.error)
+ }
+ }
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(
+ PermissionsFeature.Action.LoadPermissionApps(
+ requireArguments().getInt(
+ "PERMISSION_ID"
+ )
+ )
+ )
+ }
+ }
+
+ private fun displayToast(message: String) {
+ Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
+ .show()
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
+ setupToolbar(toolbar)
+ }
+
+ private fun setupToolbar(toolbar: Toolbar) {
+ val activity = requireActivity()
+ activity.setActionBar(toolbar)
+ activity.title = "My Apps Permission"
+ }
+
+ override fun render(state: PermissionsFeature.State) {
+ state.currentPermission?.let { permission ->
+ view?.findViewById<RecyclerView>(R.id.recylcer_view_permission_apps)?.apply {
+ val listOfPackages = mutableListOf<Pair<String, Boolean>>()
+ permission.packagesRequested.forEach {
+ listOfPackages.add(it to permission.packagesAllowed.contains(it))
+ }
+ layoutManager = LinearLayoutManager(requireContext())
+ setHasFixedSize(true)
+ adapter = PermissionAppsAdapter(listOfPackages) { packageName, grant ->
+ viewModel.submitAction(
+ PermissionsFeature.Action.TogglePermissionAction(
+ packageName,
+ grant
+ )
+ )
+ }
+ }
+ view?.findViewById<TextView>(R.id.permission_control)?.text =
+ getString(R.string.apps_access_to_permission, permission.name)
+ }
+ }
+
+ override fun actions(): Flow<PermissionsFeature.Action> = viewModel.actions
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsAdapter.kt
new file mode 100644
index 0000000..330a1ac
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsAdapter.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.permissions
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import foundation.e.privacycentralapp.R
+import foundation.e.privacycentralapp.dummy.Permission
+
+class PermissionsAdapter(
+ private val context: Context,
+ private val dataSet: List<Permission>,
+ private val listener: (Int) -> Unit
+) :
+ RecyclerView.Adapter<PermissionsAdapter.PermissionViewHolder>() {
+
+ class PermissionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val nameView: TextView = view.findViewById(R.id.permission_title)
+ val permissionCountView: TextView = view.findViewById(R.id.permission_count)
+ val permissionIcon: ImageView = view.findViewById(R.id.permission_icon)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PermissionViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_permission, parent, false)
+ val holder = PermissionViewHolder(view)
+ view.setOnClickListener { listener(holder.adapterPosition) }
+ return holder
+ }
+
+ override fun onBindViewHolder(holder: PermissionViewHolder, position: Int) {
+ val permission = dataSet[position]
+ holder.nameView.text = permission.name
+ holder.permissionCountView.text = context.getString(
+ R.string.apps_allowed,
+ permission.packagesAllowed.size,
+ permission.packagesRequested.size
+ )
+ holder.permissionIcon.setImageResource(permission.iconId)
+ }
+
+ override fun getItemCount(): Int = dataSet.size
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFeature.kt
new file mode 100644
index 0000000..d095f00
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFeature.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.permissions
+
+import android.util.Log
+import foundation.e.flowmvi.Actor
+import foundation.e.flowmvi.Reducer
+import foundation.e.flowmvi.SingleEventProducer
+import foundation.e.flowmvi.feature.BaseFeature
+import foundation.e.privacycentralapp.dummy.DummyDataSource
+import foundation.e.privacycentralapp.dummy.Permission
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+// Define a state machine for Internet privacy feature
+class PermissionsFeature(
+ initialState: State,
+ coroutineScope: CoroutineScope,
+ reducer: Reducer<State, Effect>,
+ actor: Actor<State, Action, Effect>,
+ singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent>
+) : BaseFeature<PermissionsFeature.State, PermissionsFeature.Action, PermissionsFeature.Effect, PermissionsFeature.SingleEvent>(
+ initialState,
+ actor,
+ reducer,
+ coroutineScope,
+ { message -> Log.d("PermissionsFeature", message) },
+ singleEventProducer
+) {
+ data class State(
+ val permissions: List<Permission> = emptyList(),
+ val currentPermission: Permission? = null
+ )
+
+ sealed class SingleEvent {
+ data class ErrorEvent(val error: String) : SingleEvent()
+ }
+
+ sealed class Action {
+ object ObservePermissions : Action()
+ data class LoadPermissionApps(val id: Int) : Action()
+ data class TogglePermissionAction(
+ val packageName: String,
+ val grant: Boolean
+ ) : Action()
+ }
+
+ sealed class Effect {
+ data class PermissionsLoadedEffect(val permissions: List<Permission>) : Effect()
+ data class PermissionLoadedEffect(val permission: Permission) : Effect()
+ object PermissionToggledEffect : Effect()
+ data class ErrorEffect(val message: String) : Effect()
+ }
+
+ companion object {
+ fun create(
+ initialState: State = State(),
+ coroutineScope: CoroutineScope
+ ) = PermissionsFeature(
+ initialState, coroutineScope,
+ reducer = { state, effect ->
+ when (effect) {
+ is Effect.PermissionsLoadedEffect -> State(effect.permissions)
+ is Effect.PermissionLoadedEffect -> state.copy(currentPermission = effect.permission)
+ is Effect.ErrorEffect -> state
+ Effect.PermissionToggledEffect -> state
+ }
+ },
+ actor = { state, action ->
+ when (action) {
+ Action.ObservePermissions -> DummyDataSource.populatedPermission.map {
+ Effect.PermissionsLoadedEffect(it)
+ }
+ is Action.LoadPermissionApps -> flowOf(
+ Effect.PermissionLoadedEffect(
+ DummyDataSource.getPermission(action.id)
+ )
+ )
+
+ is Action.TogglePermissionAction -> {
+ if (state.currentPermission != null) {
+ DummyDataSource.togglePermission(
+ state.currentPermission.id,
+ action.packageName,
+ action.grant
+ )
+ flowOf(Effect.PermissionToggledEffect)
+ } else {
+ flowOf(Effect.ErrorEffect("Can't update permission"))
+ }
+ }
+ }
+ },
+ singleEventProducer = { _, _, effect ->
+ when (effect) {
+ is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
+ else -> null
+ }
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFragment.kt
new file mode 100644
index 0000000..864a355
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsFragment.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.permissions
+
+import android.os.Bundle
+import android.view.View
+import android.widget.Toolbar
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.add
+import androidx.fragment.app.commit
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import foundation.e.flowmvi.MVIView
+import foundation.e.privacycentralapp.R
+import kotlinx.coroutines.flow.Flow
+
+class PermissionsFragment :
+ Fragment(R.layout.fragment_permissions),
+ MVIView<PermissionsFeature.State, PermissionsFeature.Action> {
+
+ private val viewModel: PermissionsViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycleScope.launchWhenStarted {
+ viewModel.permissionsFeature.takeView(this, this@PermissionsFragment)
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(PermissionsFeature.Action.ObservePermissions)
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
+ setupToolbar(toolbar)
+ }
+
+ private fun setupToolbar(toolbar: Toolbar) {
+ val activity = requireActivity()
+ activity.setActionBar(toolbar)
+ activity.title = "My Apps Permission"
+ }
+
+ override fun render(state: PermissionsFeature.State) {
+ view?.findViewById<RecyclerView>(R.id.recylcer_view_permissions)?.apply {
+ layoutManager = LinearLayoutManager(requireContext())
+ setHasFixedSize(true)
+ adapter = PermissionsAdapter(requireContext(), state.permissions) { permissionId ->
+ requireActivity().supportFragmentManager.commit {
+ val bundle = bundleOf("PERMISSION_ID" to permissionId)
+ add<PermissionAppsFragment>(R.id.container, args = bundle)
+ setReorderingAllowed(true)
+ addToBackStack("permissions")
+ }
+ }
+ }
+ }
+
+ override fun actions(): Flow<PermissionsFeature.Action> = viewModel.actions
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsViewModel.kt
new file mode 100644
index 0000000..fc50c39
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionsViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.features.permissions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+class PermissionsViewModel : ViewModel() {
+
+ private val _actions = MutableSharedFlow<PermissionsFeature.Action>()
+ val actions = _actions.asSharedFlow()
+
+ val permissionsFeature: PermissionsFeature by lazy {
+ PermissionsFeature.create(coroutineScope = viewModelScope)
+ }
+
+ fun submitAction(action: PermissionsFeature.Action) {
+ viewModelScope.launch {
+ _actions.emit(action)
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
new file mode 100644
index 0000000..1b92cb2
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.main
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.add
+import androidx.fragment.app.commit
+import foundation.e.privacycentralapp.R
+import foundation.e.privacycentralapp.features.dashboard.DashboardFragment
+
+open class MainActivity : FragmentActivity(R.layout.activity_main) {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (savedInstanceState == null) {
+ supportFragmentManager.commit {
+ setReorderingAllowed(true)
+ add<DashboardFragment>(R.id.container)
+ }
+ }
+ }
+
+ override fun onPostCreate(savedInstanceState: Bundle?) {
+ super.onPostCreate(savedInstanceState)
+ handleIntent(intent)
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ handleIntent(intent)
+ }
+
+ open fun handleIntent(intent: Intent) {}
+
+ override fun finishAfterTransition() {
+ val resultData = Intent()
+ val result = onPopulateResultIntent(resultData)
+ setResult(result, resultData)
+
+ super.finishAfterTransition()
+ }
+
+ open fun onPopulateResultIntent(intent: Intent): Int = Activity.RESULT_OK
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/main/MainViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/main/MainViewModel.kt
new file mode 100644
index 0000000..7e758b7
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/main/MainViewModel.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.main
+
+import androidx.lifecycle.ViewModel
+
+class MainViewModel : ViewModel()
diff --git a/app/src/main/res/drawable/dummy_img_map_picker.png b/app/src/main/res/drawable/dummy_img_map_picker.png
new file mode 100644
index 0000000..c1cf32b
--- /dev/null
+++ b/app/src/main/res/drawable/dummy_img_map_picker.png
Binary files differ
diff --git a/app/src/main/res/drawable/dummy_leakage_analytics.png b/app/src/main/res/drawable/dummy_leakage_analytics.png
new file mode 100644
index 0000000..5379cd4
--- /dev/null
+++ b/app/src/main/res/drawable/dummy_leakage_analytics.png
Binary files differ
diff --git a/app/src/main/res/drawable/ic_apps_permissions.xml b/app/src/main/res/drawable/ic_apps_permissions.xml
new file mode 100644
index 0000000..b7eb1ab
--- /dev/null
+++ b/app/src/main/res/drawable/ic_apps_permissions.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector android:height="23.99944dp" android:viewportHeight="42.738"
+ android:viewportWidth="42.739" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#fb3846" android:pathData="M42.74,5L42.74,37.738A5,5 0,0 1,37.74 42.738L5.001,42.738A5,5 0,0 1,0.001 37.738L0.001,5A5,5 0,0 1,5.001 0L37.74,0A5,5 0,0 1,42.74 5z"/>
+ <path android:fillColor="#fff" android:pathData="M37.541,20.967l-6.692,-2.579v-7.529a2.638,2.638 0,0 0,-1.673 -2.37l-6.972,-2.579a2.5,2.5 0,0 0,-1.743 0l-6.972,2.579a2.638,2.638 0,0 0,-1.673 2.37v7.529l-6.693,2.579a2.41,2.41 0,0 0,-1.6 2.3v7.668a2.529,2.529 0,0 0,1.324 2.3l6.972,3.486a2.66,2.66 0,0 0,2.3 0l7.25,-3.625 7.181,3.625a2.66,2.66 0,0 0,2.3 0l6.971,-3.486a2.589,2.589 0,0 0,1.394 -2.3v-7.668a2.474,2.474 0,0 0,-1.674 -2.3zM28.478,18.457l-5.926,2.231v-4.741l5.926,-2.579zM14.256,10.789l7.111,-2.719 7.111,2.719 -7.111,2.928zM20.112,31.076l-5.926,2.928v-5.507l5.926,-2.719zM20.112,23.268l-7.111,2.858 -7.111,-2.858v-0.07l7.111,-2.649 7.111,2.649zM36.844,31.076l-5.926,2.928v-5.507l5.926,-2.719zM36.844,23.268l-7.111,2.858 -7.111,-2.858v-0.07l7.111,-2.649 7.111,2.649z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_body_monitor.xml b/app/src/main/res/drawable/ic_body_monitor.xml
new file mode 100644
index 0000000..cbf0d27
--- /dev/null
+++ b/app/src/main/res/drawable/ic_body_monitor.xml
@@ -0,0 +1,19 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@color/black">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM12,12c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3s3,1.34 3,3S13.66,12 12,12z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10,8.5h1v1h-1z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11.5,8.5h1v1h-1z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13,8.5h1v1h-1z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_calendar.xml b/app/src/main/res/drawable/ic_calendar.xml
new file mode 100644
index 0000000..376c5a4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_calendar.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@color/black">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9,11L7,11v2h2v-2zM13,11h-2v2h2v-2zM17,11h-2v2h2v-2zM19,4h-1L18,2h-2v2L8,4L8,2L6,2v2L5,4c-1.11,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM19,20L5,20L5,9h14v11z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_call.xml b/app/src/main/res/drawable/ic_call.xml
new file mode 100644
index 0000000..41d8b40
--- /dev/null
+++ b/app/src/main/res/drawable/ic_call.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@color/black">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13,9h-2v2h2L13,9zM17,9h-2v2h2L17,9zM20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.58l2.2,-2.21c0.28,-0.27 0.36,-0.66 0.25,-1.01C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1L4,3c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,9v2h2L21,9h-2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_chevron_right_24dp.xml b/app/src/main/res/drawable/ic_chevron_right_24dp.xml
new file mode 100644
index 0000000..c0a38c1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_chevron_right_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:tint="#000">
+ <path android:fillColor="#FF000000"
+ android:pathData="M9.71,18.71l-1.42,-1.42l5.3,-5.29l-5.3,-5.29l1.42,-1.42l6.7,6.71z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_facebook.xml b/app/src/main/res/drawable/ic_facebook.xml
new file mode 100644
index 0000000..49597b3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_facebook.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="512dp"
+ android:height="512dp"
+ android:viewportWidth="512"
+ android:viewportHeight="512">
+ <path
+ android:pathData="M512,256c0,-141.385 -114.615,-256 -256,-256c-141.385,0 -256,114.615 -256,256c0,127.777 93.616,233.685 216,252.89l0,-178.89l-65,0l0,-74l65,0l0,-56.4c0,-64.16 38.219,-99.6 96.695,-99.6c28.009,0 57.305,5 57.305,5l0,63l-32.281,0c-31.801,0 -41.719,19.733 -41.719,39.978l0,48.022l71,0l-11.35,74l-59.65,0l0,178.89c122.385,-19.205 216,-125.113 216,-252.89Z"
+ android:fillColor="#1877f2"
+ android:fillType="nonZero"/>
+ <path
+ android:pathData="M355.65,330l11.35,-74l-71,0l0,-48.022c0,-20.245 9.917,-39.978 41.719,-39.978l32.281,0l0,-63c0,0 -29.297,-5 -57.305,-5c-58.476,0 -96.695,35.44 -96.695,99.6l0,56.4l-65,0l0,74l65,0l0,178.89c13.033,2.045 26.392,3.11 40,3.11c13.608,0 26.966,-1.065 40,-3.11l0,-178.89l59.65,0Z"
+ android:fillColor="#fff"
+ android:fillType="nonZero"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_internet_activity.xml b/app/src/main/res/drawable/ic_internet_activity.xml
new file mode 100644
index 0000000..ef34960
--- /dev/null
+++ b/app/src/main/res/drawable/ic_internet_activity.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector android:height="23.99944dp" android:viewportHeight="42.738"
+ android:viewportWidth="42.739" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#38d874" android:pathData="M42.74,5L42.74,37.738A5,5 0,0 1,37.74 42.738L5.001,42.738A5,5 0,0 1,0.001 37.738L0.001,5A5,5 0,0 1,5.001 0L37.74,0A5,5 0,0 1,42.74 5z"/>
+ <path android:fillColor="#fff" android:pathData="M36.109,7.096h-29.3a3.052,3.052 0,0 0,-3.052 3.052v22.379a3.013,3.013 0,0 0,3.052 3.052h29.3a3.052,3.052 0,0 0,3.052 -3.052v-22.374a3.094,3.094 0,0 0,-3.052 -3.057zM11.896,14.471a0.773,0.773 0,0 1,-0.763 0.763h-2.543a0.735,0.735 0,0 1,-0.763 -0.763v-2.543a0.773,0.773 0,0 1,0.763 -0.763h2.543a0.82,0.82 0,0 1,0.763 0.763zM35.096,14.471a0.773,0.773 0,0 1,-0.763 0.763h-15.768a0.735,0.735 0,0 1,-0.763 -0.763v-2.543a0.773,0.773 0,0 1,0.763 -0.763h15.768a0.82,0.82 0,0 1,0.763 0.763z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml
new file mode 100644
index 0000000..e117e22
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@color/black">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_my_location.xml b/app/src/main/res/drawable/ic_my_location.xml
new file mode 100644
index 0000000..3b04dc4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_my_location.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector android:height="23.99944dp" android:viewportHeight="42.738"
+ android:viewportWidth="42.739" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#fc7222" android:pathData="M42.74,5L42.74,37.738A5,5 0,0 1,37.74 42.738L5.001,42.738A5,5 0,0 1,0.001 37.738L0.001,5A5,5 0,0 1,5.001 0L37.74,0A5,5 0,0 1,42.74 5z"/>
+ <path android:fillColor="#fff" android:pathData="M21.369,3.651a13.318,13.318 0,0 0,-13.271 13.27c0,5.322 1.728,6.912 11.888,21.5a1.685,1.685 0,0 0,2.7 0c10.16,-14.584 11.957,-16.173 11.957,-21.5a13.364,13.364 0,0 0,-13.271 -13.27zM21.369,27.98a2.183,2.183 0,0 1,-2.212 -2.212,2.227 2.227,0 0,1 2.212,-2.212 2.274,2.274 0,0 1,2.212 2.212,2.227 2.227,0 0,1 -2.212,2.212zM23.169,20.17v0.138a1.079,1.079 0,0 1,-1.106 1.037h-1.106a1.079,1.079 0,0 1,-1.106 -1.037v-1.175a1.562,1.562 0,0 1,0.968 -1.451c2.074,-1.037 3.456,-1.866 3.456,-2.972a2.722,2.722 0,0 0,-2.773 -2.765,2.737 2.737,0 0,0 -2.626,2 1.038,1.038 0,0 1,-1.037 0.76h-1.175a1.112,1.112 0,0 1,-1.106 -1.313,6.146 6.146,0 0,1 5.944,-4.764 6.038,6.038 0,0 1,6.082 6.082c0,2.834 -2.281,4.354 -4.424,5.46z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_privacy_toggle.xml b/app/src/main/res/drawable/ic_privacy_toggle.xml
new file mode 100644
index 0000000..6a0f647
--- /dev/null
+++ b/app/src/main/res/drawable/ic_privacy_toggle.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector android:height="84dp" android:viewportHeight="84.406"
+ android:viewportWidth="84.406" android:width="84dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#38d874" android:pathData="M84.406,42L84.406,42.406A42,42 0,0 1,42.406 84.406L42,84.406A42,42 0,0 1,0 42.406L0,42A42,42 0,0 1,42 0L42.406,0A42,42 0,0 1,84.406 42z"/>
+ <path android:fillColor="#fff" android:pathData="M42.118,13.695a6.476,6.476 0,0 0,-2.114 0.334l-21.358,8.9a5.324,5.324 0,0 0,-3.225 4.9c0,22.136 12.68,37.376 24.583,42.382a5.284,5.284 0,0 0,4.115 0c9.566,-4 24.7,-17.687 24.7,-42.382a5.432,5.432 0,0 0,-3.338 -4.9l-21.358,-8.9a6.3,6.3 0,0 0,-2.005 -0.334zM41.1739,27.31h1.888a1.432,1.432 0,0 1,1.413 1.413v14.142a1.4,1.4 0,0 1,-1.413 1.416h-1.888a1.362,1.362 0,0 1,-1.413 -1.416L39.7608,28.72a1.394,1.394 0,0 1,1.411 -1.41zM34.1319,30.292a1.4,1.4 0,0 1,1.5 0.67l0.943,1.65a1.432,1.432 0,0 1,-0.351 1.827,9.9 9.9,0 0,0 5.89,17.854 9.933,9.933 0,0 0,9.9 -9.958,10.115 10.115,0 0,0 -4.065,-7.9 1.437,1.437 0,0 1,-0.354 -1.827l0.944,-1.65a1.4,1.4 0,0 1,2.059 -0.412,14.585 14.585,0 1,1 -17.032,0 1.432,1.432 0,0 1,0.562 -0.258z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_tracked.xml b/app/src/main/res/drawable/ic_tracked.xml
new file mode 100644
index 0000000..9aa4736
--- /dev/null
+++ b/app/src/main/res/drawable/ic_tracked.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<vector android:height="23.99944dp" android:viewportHeight="42.738"
+ android:viewportWidth="42.739" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#007fff" android:pathData="M42.74,5L42.74,37.738A5,5 0,0 1,37.74 42.738L5.001,42.738A5,5 0,0 1,0.001 37.738L0.001,5A5,5 0,0 1,5.001 0L37.74,0A5,5 0,0 1,42.74 5z"/>
+ <path android:fillColor="#fff" android:pathData="M27.929,25.118h-3.749a1.887,1.887 0,0 0,-1.875 1.874v7.5a1.85,1.85 0,0 0,1.875 1.874h3.749a1.887,1.887 0,0 0,1.874 -1.874v-7.5a1.927,1.927 0,0 0,-1.874 -1.874zM26.992,33.553h-1.874v-5.623h1.874zM37.302,17.621h-3.749a1.887,1.887 0,0 0,-1.875 1.874v15a1.85,1.85 0,0 0,1.875 1.874h3.749a1.888,1.888 0,0 0,1.875 -1.874v-15a1.927,1.927 0,0 0,-1.875 -1.874zM36.365,33.553h-1.875v-13.121h1.875zM18.5579,17.621h-3.749a1.887,1.887 0,0 0,-1.875 1.874v15a1.85,1.85 0,0 0,1.875 1.874h3.749a1.888,1.888 0,0 0,1.875 -1.874v-15a1.927,1.927 0,0 0,-1.875 -1.874zM17.6209,33.553h-1.874v-13.121h1.874zM9.1859,26.993h-3.751a1.887,1.887 0,0 0,-1.875 1.874v5.623a1.851,1.851 0,0 0,1.875 1.874h3.749a1.887,1.887 0,0 0,1.874 -1.874v-5.623a1.927,1.927 0,0 0,-1.874 -1.875zM8.2489,33.553h-1.875v-3.749h1.875zM7.3119,21.369a2.812,2.812 0,0 0,2.812 -2.812,2.941 2.941,0 0,0 -0.117,-0.7l5.916,-5.916a3.094,3.094 0,0 0,0.762 0.059,2.974 2.974,0 0,0 1,-0.176l5.565,4.452v0.41a2.776,2.776 0,0 0,2.812 2.812,2.812 2.812,0 0,0 2.812,-2.812c0,-0.117 -0.059,-0.234 -0.059,-0.41l5.565,-4.452a3.064,3.064 0,0 0,1.054 0.176,2.812 2.812,0 0,0 2.812,-2.812 2.851,2.851 0,0 0,-2.812 -2.812,2.812 2.812,0 0,0 -2.812,2.812v0.469l-5.565,4.452a3.081,3.081 0,0 0,-1.054 -0.234,3.077 3.077,0 0,0 -1,0.234l-5.565,-4.452c0,-0.176 0.059,-0.293 0.059,-0.469a2.851,2.851 0,0 0,-2.812 -2.812,2.812 2.812,0 0,0 -2.812,2.812 3.092,3.092 0,0 0,0.059 0.761l-5.916,5.916a2.943,2.943 0,0 0,-0.7 -0.117,2.812 2.812,0 0,0 -2.812,2.812 2.776,2.776 0,0 0,2.806 2.809z"/>
+</vector>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 4fc2444..7013496 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,18 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
+<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".MainActivity">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Hello World!"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
-</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
+ /> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
new file mode 100644
index 0000000..663c270
--- /dev/null
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -0,0 +1,350 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top|center"
+ android:background="@color/white"
+ tools:layout_height="56dp"
+ />
+
+ <ProgressBar
+ android:id="@+id/loadingSpinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ />
+
+ <androidx.core.widget.NestedScrollView
+ android:id="@+id/scrollContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="?android:attr/actionBarSize"
+ android:visibility="gone"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ tools:context=".main.MainActivity"
+ >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center"
+ android:paddingLeft="32dp"
+ android:paddingTop="16dp"
+ android:paddingRight="32dp"
+ android:paddingBottom="16dp"
+ android:text="@string/privacy_dashboard_info"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <ImageView
+ android:id="@+id/togglePrivacyCentral"
+ android:layout_width="96dp"
+ android:layout_height="96dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:src="@drawable/ic_privacy_toggle"
+ />
+
+ <TextView
+ android:id="@+id/tap_to_enable_quick_protection"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:drawableEnd="@drawable/ic_chevron_right_24dp"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center"
+ android:paddingLeft="32dp"
+ android:paddingTop="16dp"
+ android:paddingRight="32dp"
+ android:paddingBottom="16dp"
+ android:text="@string/tap_to_enable_quick_protection"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="160dp"
+ android:src="@drawable/dummy_leakage_analytics"
+ />
+
+ <TextView
+ android:id="@+id/personal_leakag_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ android:paddingBottom="16dp"
+ android:text="@string/personal_leakage_info"
+ android:textColor="@color/black"
+ android:textSize="12sp"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#f9f9f9"
+ android:orientation="vertical"
+ >
+
+ <RelativeLayout
+ android:id="@+id/am_i_tracked"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="32dp"
+ android:paddingTop="16dp"
+ android:paddingRight="32dp"
+ android:paddingBottom="16dp"
+ >
+
+ <ImageView
+ android:id="@+id/am_i_tracked_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_tracked"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toStartOf="@+id/am_i_tracked_chevron"
+ android:layout_toEndOf="@+id/am_i_tracked_icon"
+ android:orientation="vertical"
+ android:paddingStart="16dp"
+ android:paddingEnd="32dp"
+ >
+
+ <TextView
+ android:id="@+id/am_i_tracked_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/am_i_tracked_title"
+ android:textColor="@color/black"
+ android:textSize="16sp"
+ />
+
+ <TextView
+ android:id="@+id/am_i_tracked_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/am_i_tracked_subtitle"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/am_i_tracked_chevron"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_chevron_right_24dp"
+ />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/apps_permissions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="32dp"
+ android:paddingTop="16dp"
+ android:paddingRight="32dp"
+ android:paddingBottom="16dp"
+ >
+
+ <ImageView
+ android:id="@+id/apps_permissions_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_apps_permissions"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toStartOf="@+id/apps_permissions_chevron"
+ android:layout_toEndOf="@+id/apps_permissions_icon"
+ android:orientation="vertical"
+ android:paddingStart="16dp"
+ android:paddingEnd="32dp"
+ >
+
+ <TextView
+ android:id="@+id/apps_permissions_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/apps_permissions_title"
+ android:textColor="@color/black"
+ android:textSize="16sp"
+ />
+
+ <TextView
+ android:id="@+id/apps_permissions_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/apps_permissions_subtitle"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/apps_permissions_chevron"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_chevron_right_24dp"
+ />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/my_location"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="32dp"
+ android:paddingTop="16dp"
+ android:paddingRight="32dp"
+ android:paddingBottom="16dp"
+ >
+
+ <ImageView
+ android:id="@+id/my_location_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_my_location"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toStartOf="@+id/my_location_chevron"
+ android:layout_toEndOf="@+id/my_location_icon"
+ android:orientation="vertical"
+ android:paddingStart="16dp"
+ android:paddingEnd="32dp"
+ >
+
+ <TextView
+ android:id="@+id/my_location_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/my_location_title"
+ android:textColor="@color/black"
+ android:textSize="16sp"
+ />
+
+ <TextView
+ android:id="@+id/my_location_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/my_location_subtitle"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/my_location_chevron"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_chevron_right_24dp"
+ />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/internet_activity_privacy"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="32dp"
+ android:paddingTop="16dp"
+ android:paddingRight="32dp"
+ android:paddingBottom="16dp"
+ >
+
+ <ImageView
+ android:id="@+id/internet_activity_privacy_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_internet_activity"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toStartOf="@+id/internet_activity_privacy_chevron"
+ android:layout_toEndOf="@+id/internet_activity_privacy_icon"
+ android:orientation="vertical"
+ android:paddingStart="16dp"
+ android:paddingEnd="32dp"
+ >
+
+ <TextView
+ android:id="@+id/internet_activity_privacy_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/internet_activity_privacy_title"
+ android:textColor="@color/black"
+ android:textSize="16sp"
+ />
+
+ <TextView
+ android:id="@+id/internet_activity_privacy_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/internet_activity_privacy_subtitle"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/internet_activity_privacy_chevron"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_chevron_right_24dp"
+ />
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+ </androidx.core.widget.NestedScrollView>
+</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_fake_location.xml b/app/src/main/res/layout/fragment_fake_location.xml
new file mode 100644
index 0000000..1ebe9ef
--- /dev/null
+++ b/app/src/main/res/layout/fragment_fake_location.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ >
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top|center"
+ android:background="@color/white"
+ tools:layout_height="56dp"
+ />
+
+ <androidx.core.widget.NestedScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="?android:attr/actionBarSize"
+ android:layout_marginBottom="32dp"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ tools:context=".main.MainActivity"
+ >
+
+ <TextView
+ android:id="@+id/fake_location_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="16dp"
+ android:text="@string/fake_location_info"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <TextView
+ android:id="@+id/learn_more_fake_location"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center_vertical"
+ android:text="@string/learn_more"
+ android:textColor="#007fff"
+ android:textSize="14sp"
+ />
+
+ <TextView
+ android:id="@+id/my_location_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:text="@string/my_location_title"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <RadioGroup
+ android:id="@+id/location_choices"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <foundation.e.privacycentralapp.common.RightRadioButton
+ android:id="@+id/radio_use_real_location"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/use_real_location"
+ android:textSize="16sp"
+ />
+
+ <foundation.e.privacycentralapp.common.RightRadioButton
+ android:id="@+id/radio_use_random_location"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/use_random_location"
+ android:textSize="16sp"
+ />
+
+ <foundation.e.privacycentralapp.common.RightRadioButton
+ android:id="@+id/radio_use_specific_location"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/use_specific_location"
+ android:textSize="16sp"
+ />
+ </RadioGroup>
+
+ <ImageView
+ android:id="@+id/dummy_img_map"
+ android:layout_width="match_parent"
+ android:layout_height="254dp"
+ android:layout_marginTop="32dp"
+ android:layout_marginBottom="32dp"
+ android:src="@drawable/dummy_img_map_picker"
+ />
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/edittext_longitude"
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/longitude"
+ >
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="numberDecimal"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/edittext_latitude"
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:hint="@string/latitude"
+ >
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="numberDecimal"
+ />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <Button
+ android:id="@+id/button_add_location"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:text="@string/add_location"
+ app:backgroundTint="#007fff"
+ android:layout_marginTop="32dp"
+ />
+ </LinearLayout>
+ </androidx.core.widget.NestedScrollView>
+</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_internet_activity_policy.xml b/app/src/main/res/layout/fragment_internet_activity_policy.xml
new file mode 100644
index 0000000..6a53498
--- /dev/null
+++ b/app/src/main/res/layout/fragment_internet_activity_policy.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ >
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top|center"
+ android:background="@color/white"
+ tools:layout_height="56dp"
+ />
+
+ <androidx.core.widget.NestedScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="?android:attr/actionBarSize"
+ android:layout_marginBottom="32dp"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ tools:context=".main.MainActivity"
+ >
+
+ <TextView
+ android:id="@+id/internet_activity_privacy_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="16dp"
+ android:text="@string/internet_activity_privacy_info"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <TextView
+ android:id="@+id/learn_more_internet_activity_privacy_info"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center_vertical"
+ android:text="@string/learn_more"
+ android:textColor="#007fff"
+ android:textSize="14sp"
+ />
+
+ <TextView
+ android:id="@+id/my_internet_activity_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:text="@string/internet_activity_privacy_title"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <RadioGroup
+ android:id="@+id/internet_activity_privacy_choices"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <foundation.e.privacycentralapp.common.RightRadioButton
+ android:id="@+id/radio_use_real_ip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/use_real_ip"
+ android:textSize="16sp"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/i_can_be_tracked"
+ android:textSize="14sp"/>
+
+ <foundation.e.privacycentralapp.common.RightRadioButton
+ android:id="@+id/radio_use_hidden_ip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hidden_ip"
+ android:textSize="16sp"
+ android:layout_marginTop="8dp"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/i_am_anonymous"
+ android:textSize="14sp"/>
+ </RadioGroup>
+ </LinearLayout>
+ </androidx.core.widget.NestedScrollView>
+</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_permission_apps.xml b/app/src/main/res/layout/fragment_permission_apps.xml
new file mode 100644
index 0000000..2888af0
--- /dev/null
+++ b/app/src/main/res/layout/fragment_permission_apps.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ >
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top|center"
+ android:background="@color/white"
+ tools:layout_height="56dp"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_marginTop="?android:attr/actionBarSize"
+ tools:context=".main.MainActivity"
+ >
+
+ <TextView
+ android:id="@+id/permission_control"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/item_permission_apps"
+ android:id="@+id/recylcer_view_permission_apps"/>
+ </LinearLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_permissions.xml b/app/src/main/res/layout/fragment_permissions.xml
new file mode 100644
index 0000000..9d1e972
--- /dev/null
+++ b/app/src/main/res/layout/fragment_permissions.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ >
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top|center"
+ android:background="@color/white"
+ tools:layout_height="56dp"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ android:layout_marginTop="?android:attr/actionBarSize"
+ tools:context=".main.MainActivity"
+ >
+
+ <TextView
+ android:id="@+id/permission_control"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="16dp"
+ android:text="@string/permission_control_info"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <TextView
+ android:id="@+id/learn_more_permissions"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center_vertical"
+ android:text="@string/learn_more"
+ android:textColor="#007fff"
+ android:textSize="14sp"
+ />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/item_permission"
+ android:id="@+id/recylcer_view_permissions"/>
+ </LinearLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_quick_protection.xml b/app/src/main/res/layout/fragment_quick_protection.xml
new file mode 100644
index 0000000..e8233ee
--- /dev/null
+++ b/app/src/main/res/layout/fragment_quick_protection.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ >
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top|center"
+ android:background="@color/white"
+ tools:layout_height="56dp"
+ />
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_marginTop="?android:attr/actionBarSize"
+ android:layout_marginBottom="56dp"
+ tools:context=".main.MainActivity"
+ >
+
+ <TextView
+ android:id="@+id/quick_protection_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ android:paddingTop="16dp"
+ android:text="@string/quick_protection_info"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <TextView
+ android:id="@+id/quick_protection_settings_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/quick_protection_settings_list"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="56dp"
+ android:paddingRight="32dp"
+ android:paddingLeft="32dp"
+ android:gravity="center_vertical|right"
+ android:id="@+id/learn_more"
+ android:text="@string/learn_more"
+ android:textColor="#007fff"
+ android:textSize="14sp"
+ android:fontFamily="sans-serif-medium"
+ android:layout_gravity="bottom|right"/>
+</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/item_permission.xml b/app/src/main/res/layout/item_permission.xml
new file mode 100644
index 0000000..8f54f64
--- /dev/null
+++ b/app/src/main/res/layout/item_permission.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/item_permission"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ >
+
+ <ImageView
+ android:id="@+id/permission_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_body_monitor"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toStartOf="@+id/chevron"
+ android:layout_toEndOf="@+id/permission_icon"
+ android:orientation="vertical"
+ android:paddingStart="32dp"
+ android:paddingEnd="32dp"
+ >
+
+ <TextView
+ android:id="@+id/permission_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ tools:text="Body sensor"
+ android:textColor="@color/black"
+ android:textSize="16sp"
+ />
+
+ <TextView
+ android:id="@+id/permission_count"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:text="3 of 8 apps allowed"
+ android:textSize="14sp"
+ />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/chevron"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:padding="4dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_chevron_right_24dp"
+ />
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/item_permission_apps.xml b/app/src/main/res/layout/item_permission_apps.xml
new file mode 100644
index 0000000..aec8fec
--- /dev/null
+++ b/app/src/main/res/layout/item_permission_apps.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/item_permission_apps"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ >
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_facebook"
+ />
+
+ <TextView
+ android:id="@+id/app_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toStartOf="@+id/togglePermission"
+ android:layout_toEndOf="@+id/app_icon"
+ android:fontFamily="sans-serif-medium"
+ android:paddingStart="32dp"
+ android:paddingEnd="32dp"
+ android:textColor="@color/black"
+ android:textSize="16sp"
+ tools:text="Body sensor"
+ />
+
+ <Switch
+ android:id="@+id/togglePermission"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ />
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index 647c8ce..0000000
--- a/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<resources xmlns:tools="http://schemas.android.com/tools">
- <!-- Base application theme. -->
- <style name="Theme.PrivacyCentralApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
- <!-- Primary brand color. -->
- <item name="colorPrimary">@color/purple_200</item>
- <item name="colorPrimaryVariant">@color/purple_700</item>
- <item name="colorOnPrimary">@color/black</item>
- <!-- Secondary brand color. -->
- <item name="colorSecondary">@color/teal_200</item>
- <item name="colorSecondaryVariant">@color/teal_200</item>
- <item name="colorOnSecondary">@color/black</item>
- <!-- Status bar color. -->
- <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
- <!-- Customize your theme here. -->
- </style>
-</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fdb9ac8..ff0cf0a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,38 @@
<resources>
<string name="app_name">PrivacyCentralApp</string>
+ <string name="privacy_dashboard_info">Privacy dashboard helps you control and better protect your privacy</string>
+ <string name="tap_to_enable_quick_protection">Tap to enable quick privacy protection</string>
+ <string name="personal_leakage_info">Personal data leakage over past 24 hours. </string>
+ <string name="am_i_tracked_title">Am I tracked?</string>
+ <string name="am_i_tracked_subtitle">Currently there are %1$d trackers in your apps, %2$d trackers are active</string>
+ <string name="apps_permissions_title">Apps Permissions</string>
+ <string name="apps_permissions_subtitle">%1$d apps are requesting %2$d permissions</string>
+ <string name="my_location_title">My Location</string>
+ <string name="my_location_subtitle">"%1$d apps are using location permission\nCurrent location mode: "</string>
+ <string name="internet_activity_privacy_title">My Internet Activity Privacy</string>
+ <string name="internet_activity_privacy_subtitle">"Current internet activity mode: "</string>
+ <string name="quick_protection_info">Quick protection enables these settings when turned on</string>
+ <string name="quick_protection_settings_list"> - All trackers are turned off.\n- Your geolocation will be faked.\n- Your real IP address will be hidden.</string>
+ <string name="learn_more">Learn more</string>
+ <string name="fake_location_info">Choose if you want to fake your real location when app asks for your geolocation tracking.</string>
+ <string name="use_real_location">Use real location</string>
+ <string name="use_random_location">Use random plausible location</string>
+ <string name="use_specific_location">Use specific location</string>
+ <string name="longitude">Longitude</string>
+ <string name="latitude">Latitude</string>
+ <string name="add_location">Add location</string>
+ <string name="internet_activity_privacy_info">Choose if you want to expose your real IP address or hide when connected to the internet (uses the tor network).</string>
+ <string name="use_real_ip">Use real IP address</string>
+ <string name="i_can_be_tracked">I can be tracked by my IP address</string>
+ <string name="hidden_ip">Hide IP address</string>
+ <string name="i_am_anonymous">I am anonymous on the internet</string>
+ <string name="i_am_exposing">I am exposing my real IP address</string>
+ <string name="permission_control_info">Manage and control apps requesting various permissions.</string>
+
+ <string name="apps_allowed">%1$d of %2$d apps allowed</string>
+ <string name="apps_access_to_permission">Apps which has access to %1$s permission</string>
+
+ <string name="real_location_mode">Real location mode</string>
+ <string name="random_location_mode">Random location mode</string>
+ <string name="fake_location_mode">Fake location mode</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 29ff5b8..3a7bad8 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,16 +1,12 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
- <style name="Theme.PrivacyCentralApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <style name="Theme.PrivacyCentralApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
- <item name="colorPrimary">@color/purple_500</item>
- <item name="colorPrimaryVariant">@color/purple_700</item>
- <item name="colorOnPrimary">@color/white</item>
- <!-- Secondary brand color. -->
- <item name="colorSecondary">@color/teal_200</item>
- <item name="colorSecondaryVariant">@color/teal_700</item>
- <item name="colorOnSecondary">@color/black</item>
+ <item name="colorPrimary">#007fff</item>
+ <item name="colorAccent">#007fff</item>
+ <item name="colorSecondary">#007fff</item>
+ <item name="colorControlNormal">#007fff</item>
<!-- Status bar color. -->
- <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources> \ No newline at end of file