summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
authorjacquarg <guillaume.jacquart@hoodbrains.com>2021-10-29 16:44:39 +0200
committerjacquarg <guillaume.jacquart@hoodbrains.com>2021-10-29 18:00:47 +0200
commit366e4ffa04e8d301794e613b89ed918df0b59517 (patch)
treee956d7f30128ef6f5bdd2494be288ee1cb41ce20 /app/src
parent74fb672978043886e261eb66c47658caf05812bb (diff)
Update IPScrambling UI
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt6
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt3
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/ToggleAppsAdapter.kt24
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt51
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt69
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt192
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt16
-rw-r--r--app/src/main/res/drawable/ic_ic_chevron_left_24dp.xml10
-rw-r--r--app/src/main/res/drawable/outlined_background.xml22
-rw-r--r--app/src/main/res/layout/fragment_internet_activity_policy.xml159
-rw-r--r--app/src/main/res/layout/ipscrambling_item_app_toggle.xml40
-rw-r--r--app/src/main/res/layout/ipscrambling_item_selectmode.xml57
-rw-r--r--app/src/main/res/values/colors.xml1
-rw-r--r--app/src/main/res/values/strings.xml23
14 files changed, 371 insertions, 302 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index 094ec3e..1f7cd3d 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -21,6 +21,7 @@ import android.app.Application
import android.content.Context
import android.os.Process
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
+import foundation.e.privacycentralapp.domain.usecases.AppListUseCase
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacycentralapp.features.dashboard.DashBoardViewModelFactory
@@ -73,6 +74,9 @@ class DependencyContainer constructor(val app: Application) {
private val ipScramblingStateUseCase by lazy {
IpScramblingStateUseCase(ipScramblerModule, localStateRepository, GlobalScope)
}
+ private val appListUseCase by lazy {
+ AppListUseCase(permissionsModule)
+ }
val dashBoardViewModelFactory by lazy {
DashBoardViewModelFactory(getQuickPrivacyStateUseCase, ipScramblingStateUseCase)
@@ -85,6 +89,6 @@ class DependencyContainer constructor(val app: Application) {
val blockerService = BlockerInterface.getInstance(context)
val internetPrivacyViewModelFactory by lazy {
- InternetPrivacyViewModelFactory(ipScramblerModule, permissionsModule, getQuickPrivacyStateUseCase, ipScramblingStateUseCase)
+ InternetPrivacyViewModelFactory(ipScramblerModule, getQuickPrivacyStateUseCase, ipScramblingStateUseCase, appListUseCase)
}
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt
index 52197cd..5930fff 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt
@@ -19,13 +19,14 @@ package foundation.e.privacycentralapp.common
import androidx.annotation.LayoutRes
import com.google.android.material.appbar.MaterialToolbar
+import foundation.e.privacycentralapp.R
abstract class NavToolbarFragment(@LayoutRes contentLayoutId: Int) : ToolbarFragment(contentLayoutId) {
override fun setupToolbar(toolbar: MaterialToolbar) {
super.setupToolbar(toolbar)
toolbar.apply {
- setNavigationIcon(lineageos.platform.R.drawable.ic_back)
+ setNavigationIcon(R.drawable.ic_ic_chevron_left_24dp)
setNavigationOnClickListener {
requireActivity().onBackPressed()
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/ToggleAppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/common/ToggleAppsAdapter.kt
index 4f9a6fc..71b5e97 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/common/ToggleAppsAdapter.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/common/ToggleAppsAdapter.kt
@@ -17,10 +17,10 @@
package foundation.e.privacycentralapp.common
-import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.CheckBox
import android.widget.ImageView
import android.widget.Switch
import android.widget.TextView
@@ -28,22 +28,22 @@ import androidx.recyclerview.widget.RecyclerView
import foundation.e.privacycentralapp.R
import foundation.e.privacymodules.permissions.data.ApplicationDescription
-open class ToggleAppsAdapter(
+class ToggleAppsAdapter(
+ private val itemsLayout: Int,
private val listener: (String, Boolean) -> Unit
) :
- RecyclerView.Adapter<ToggleAppsAdapter.PermissionViewHolder>() {
+ RecyclerView.Adapter<ToggleAppsAdapter.ViewHolder>() {
- class PermissionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
- val appName: TextView = view.findViewById(R.id.app_title)
+ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val appName: TextView = view.findViewById(R.id.title)
- @SuppressLint("UseSwitchCompatOrMaterialCode")
- val togglePermission: Switch = view.findViewById(R.id.toggle)
+ val togglePermission: CheckBox = view.findViewById(R.id.toggle)
fun bind(item: Pair<ApplicationDescription, Boolean>) {
appName.text = item.first.label
togglePermission.isChecked = item.second
- itemView.findViewById<ImageView>(R.id.app_icon).setImageDrawable(item.first.icon)
+ itemView.findViewById<ImageView>(R.id.icon).setImageDrawable(item.first.icon)
}
}
@@ -53,10 +53,10 @@ open class ToggleAppsAdapter(
notifyDataSetChanged()
}
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PermissionViewHolder {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
- .inflate(R.layout.item_app_toggle, parent, false)
- val holder = PermissionViewHolder(view)
+ .inflate(itemsLayout, parent, false)
+ val holder = ViewHolder(view)
holder.togglePermission.setOnCheckedChangeListener { _, isChecked ->
listener(dataSet[holder.adapterPosition].first.packageName, isChecked)
}
@@ -64,7 +64,7 @@ open class ToggleAppsAdapter(
return holder
}
- override fun onBindViewHolder(holder: PermissionViewHolder, position: Int) {
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val permission = dataSet[position]
holder.bind(permission)
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt
new file mode 100644
index 0000000..8ce08a3
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.domain.usecases
+
+import android.Manifest
+import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+
+class AppListUseCase(
+ private val permissionsModule: PermissionsPrivacyModule
+) {
+
+ fun getAppsUsingInternet(): List<ApplicationDescription> {
+ return permissionsModule.getInstalledApplications()
+ .filter {
+ permissionsModule.getPermissions(it.packageName)
+ .contains(Manifest.permission.INTERNET)
+ }.map {
+ it.icon = permissionsModule.getApplicationIcon(it.packageName)
+ it
+ }.sortedWith(object : Comparator<ApplicationDescription> {
+ override fun compare(
+ p0: ApplicationDescription?,
+ p1: ApplicationDescription?
+ ): Int {
+ return if (p0?.icon != null && p1?.icon != null) {
+ p0.label.toString().compareTo(p1.label.toString())
+ } else if (p0?.icon == null) {
+ 1
+ } else {
+ -1
+ }
+ }
+ })
+ }
+}
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
index d8254b8..f7145d1 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt
@@ -17,7 +17,6 @@
package foundation.e.privacycentralapp.features.internetprivacy
-import android.Manifest
import android.app.Activity
import android.content.Intent
import android.util.Log
@@ -26,18 +25,20 @@ import foundation.e.flowmvi.Reducer
import foundation.e.flowmvi.SingleEventProducer
import foundation.e.flowmvi.feature.BaseFeature
import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
+import foundation.e.privacycentralapp.domain.usecases.AppListUseCase
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
-import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.ApplicationDescription
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.shareIn
// Define a state machine for Internet privacy feature
class InternetPrivacyFeature(
@@ -62,18 +63,8 @@ class InternetPrivacyFeature(
val availableLocationIds: List<String>,
val forceRedraw: Boolean = false
) {
-
- val isAllAppsScrambled get() = ipScrambledApps.isEmpty()
- fun getScrambledApps(): List<Pair<ApplicationDescription, Boolean>> {
- return availableApps
- .filter { it.packageName in ipScrambledApps }
- .map { it to true }
- }
-
fun getApps(): List<Pair<ApplicationDescription, Boolean>> {
- return availableApps
- .filter { it.packageName !in ipScrambledApps }
- .map { it to false }
+ return availableApps.map { it to (it.packageName in ipScrambledApps) }
}
val selectedLocationPosition get() = availableLocationIds.indexOf(selectedLocation)
@@ -102,7 +93,10 @@ class InternetPrivacyFeature(
object QuickPrivacyDisabledWarningEffect : Effect()
data class ShowAndroidVpnDisclaimerEffect(val intent: Intent) : Effect()
data class IpScrambledAppsUpdatedEffect(val ipScrambledApps: Collection<String>) : Effect()
- data class AvailableAppsListEffect(val apps: List<ApplicationDescription>) : Effect()
+ data class AvailableAppsListEffect(
+ val apps: List<ApplicationDescription>,
+ val ipScrambledApps: Collection<String>
+ ) : Effect()
data class LocationSelectedEffect(val locationId: String) : Effect()
data class AvailableCountriesEffect(val availableLocationsIds: List<String>) : Effect()
data class ErrorEffect(val message: String) : Effect()
@@ -119,16 +113,19 @@ class InternetPrivacyFeature(
),
coroutineScope: CoroutineScope,
ipScramblerModule: IIpScramblerModule,
- permissionsModule: PermissionsPrivacyModule,
getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
- ipScramblingStateUseCase: IpScramblingStateUseCase
+ ipScramblingStateUseCase: IpScramblingStateUseCase,
+ appListUseCase: AppListUseCase
) = InternetPrivacyFeature(
initialState, coroutineScope,
reducer = { state, effect ->
when (effect) {
is Effect.ModeUpdatedEffect -> state.copy(mode = effect.mode)
is Effect.IpScrambledAppsUpdatedEffect -> state.copy(ipScrambledApps = effect.ipScrambledApps)
- is Effect.AvailableAppsListEffect -> state.copy(availableApps = effect.apps)
+ is Effect.AvailableAppsListEffect -> state.copy(
+ availableApps = effect.apps,
+ ipScrambledApps = effect.ipScrambledApps
+ )
is Effect.AvailableCountriesEffect -> state.copy(availableLocationIds = effect.availableLocationsIds)
is Effect.LocationSelectedEffect -> state.copy(selectedLocation = effect.locationId)
Effect.QuickPrivacyDisabledWarningEffect -> state.copy(forceRedraw = !state.forceRedraw)
@@ -139,33 +136,21 @@ class InternetPrivacyFeature(
when {
action is Action.LoadInternetModeAction -> merge(
getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow.map { Effect.QuickPrivacyUpdatedEffect(it) },
- ipScramblingStateUseCase.internetPrivacyMode.map { Effect.ModeUpdatedEffect(it) },
+ flowOf(Effect.QuickPrivacyUpdatedEffect(true)),
+ ipScramblingStateUseCase.internetPrivacyMode.map { Effect.ModeUpdatedEffect(it) }.shareIn(scope = coroutineScope, started = SharingStarted.Lazily, replay = 0),
+ flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.REAL_IP)),
flow {
- // TODO: filter deactivated apps"
- val apps = permissionsModule.getInstalledApplications()
- .filter {
- permissionsModule.getPermissions(it.packageName)
- .contains(Manifest.permission.INTERNET)
- }.map {
- it.icon = permissionsModule.getApplicationIcon(it.packageName)
- it
- }.sortedWith(object : Comparator<ApplicationDescription> {
- override fun compare(
- p0: ApplicationDescription?,
- p1: ApplicationDescription?
- ): Int {
- return if (p0?.icon != null && p1?.icon != null) {
- p0.label.toString().compareTo(p1.label.toString())
- } else if (p0?.icon == null) {
- 1
- } else {
- -1
- }
- }
- })
- emit(Effect.AvailableAppsListEffect(apps))
+ val apps = appListUseCase.getAppsUsingInternet()
+ if (ipScramblerModule.appList.isEmpty()) {
+ ipScramblerModule.appList = apps.map { it.packageName }.toMutableSet()
+ }
+ emit(
+ Effect.AvailableAppsListEffect(
+ apps,
+ ipScramblerModule.appList
+ )
+ )
},
- flowOf(Effect.IpScrambledAppsUpdatedEffect(ipScramblerModule.appList)),
flow {
val locationIds = mutableListOf("")
locationIds.addAll(ipScramblerModule.getAvailablesLocations().sorted())
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
index 3799349..e7a9480 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt
@@ -18,20 +18,14 @@
package foundation.e.privacycentralapp.features.internetprivacy
import android.os.Bundle
-import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
-import android.widget.ProgressBar
-import android.widget.RadioButton
-import android.widget.Spinner
-import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
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.DependencyContainer
import foundation.e.privacycentralapp.PrivacyCentralApplication
@@ -69,15 +63,10 @@ class InternetPrivacyFragment :
when (event) {
is InternetPrivacyFeature.SingleEvent.ErrorEvent -> {
displayToast(event.error)
- viewModel
}
is InternetPrivacyFeature.SingleEvent.StartAndroidVpnActivityEvent -> {
- Log.d("TestsVPN", event.intent.toString())
- Log.d("TestsVPN", event.intent.action.toString())
launchAndroidVpnDisclaimer.launch(event.intent)
}
- InternetPrivacyFeature.SingleEvent.HiddenIPSelectedEvent -> displayToast("Your IP is hidden")
- InternetPrivacyFeature.SingleEvent.RealIPSelectedEvent -> displayToast("Your IP is visible to internet")
}
}
}
@@ -99,134 +88,97 @@ class InternetPrivacyFragment :
super.onViewCreated(view, savedInstanceState)
binding = FragmentInternetActivityPolicyBinding.bind(view)
- listOf(R.id.recycler_view_scrambled, R.id.recycler_view_to_select).forEach { viewId ->
- view.findViewById<RecyclerView>(viewId)?.apply {
- layoutManager = LinearLayoutManager(requireContext())
- setHasFixedSize(true)
- adapter = ToggleAppsAdapter { packageName, isIpScrambled ->
- viewModel.submitAction(
- InternetPrivacyFeature.Action.ToggleAppIpScrambled(
- packageName,
- isIpScrambled
- )
+ binding.apps.apply {
+ layoutManager = LinearLayoutManager(requireContext())
+ setHasFixedSize(true)
+ adapter = ToggleAppsAdapter(R.layout.ipscrambling_item_app_toggle) { packageName, isIpScrambled ->
+ viewModel.submitAction(
+ InternetPrivacyFeature.Action.ToggleAppIpScrambled(
+ packageName,
+ isIpScrambled
)
- }
+ )
}
}
- bindClickListeners(view)
- }
-
- override fun getTitle(): String = getString(R.string.internet_activity_privacy)
+ binding.radioUseRealIp.container.setOnClickListener {
+ viewModel.submitAction(InternetPrivacyFeature.Action.UseRealIPAction)
+ }
- private fun bindClickListeners(fragmentView: View) {
- fragmentView.let {
- it.findViewById<RadioButton>(R.id.radio_use_real_ip)
- .setOnClickListener {
- viewModel.submitAction(InternetPrivacyFeature.Action.UseRealIPAction)
- }
- it.findViewById<RadioButton>(R.id.radio_use_hidden_ip)
- .setOnClickListener {
- viewModel.submitAction(InternetPrivacyFeature.Action.UseHiddenIPAction)
- }
+ binding.radioUseHiddenIp.container.setOnClickListener {
+ viewModel.submitAction(InternetPrivacyFeature.Action.UseHiddenIPAction)
}
}
+ override fun getTitle(): String = getString(R.string.ipscrambling_title)
+
override fun render(state: InternetPrivacyFeature.State) {
- view?.let {
- it.findViewById<RadioButton>(R.id.radio_use_hidden_ip).apply {
- isChecked = state.mode in listOf(
- InternetPrivacyMode.HIDE_IP,
- InternetPrivacyMode.HIDE_IP_LOADING
- )
- isEnabled = state.mode != InternetPrivacyMode.HIDE_IP_LOADING
- }
- it.findViewById<RadioButton>(R.id.radio_use_real_ip)?.apply {
- isChecked =
- state.mode in listOf(
- InternetPrivacyMode.REAL_IP,
- InternetPrivacyMode.REAL_IP_LOADING
- )
- isEnabled = state.mode != InternetPrivacyMode.REAL_IP_LOADING
- }
- it.findViewById<TextView>(R.id.ipscrambling_tor_status)?.apply {
- when (state.mode) {
- InternetPrivacyMode.HIDE_IP_LOADING -> {
- text = getString(R.string.ipscrambling_is_starting)
- visibility = View.VISIBLE
- }
- InternetPrivacyMode.REAL_IP_LOADING -> {
- text = getString(R.string.ipscrambling_is_stopping)
- visibility = View.VISIBLE
- }
- else -> {
- text = ""
- visibility = View.GONE
+ binding.radioUseHiddenIp.radiobutton.apply {
+ isChecked = state.mode in listOf(
+ InternetPrivacyMode.HIDE_IP,
+ InternetPrivacyMode.HIDE_IP_LOADING
+ )
+ isEnabled = state.mode != InternetPrivacyMode.HIDE_IP_LOADING
+ }
+ binding.radioUseRealIp.radiobutton.apply {
+ isChecked =
+ state.mode in listOf(
+ InternetPrivacyMode.REAL_IP,
+ InternetPrivacyMode.REAL_IP_LOADING
+ )
+ isEnabled = state.mode != InternetPrivacyMode.REAL_IP_LOADING
+ }
+
+ binding.ipscramblingSelectLocation.apply {
+ adapter = ArrayAdapter(
+ requireContext(), android.R.layout.simple_spinner_item,
+ state.availableLocationIds.map {
+ if (it == "") {
+ getString(R.string.ipscrambling_any_location)
+ } else {
+ Locale("", it).displayCountry
}
}
+ ).apply {
+ setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
- it.findViewById<Spinner>(R.id.ipscrambling_select_location)?.apply {
- adapter = ArrayAdapter(
- requireContext(), android.R.layout.simple_spinner_item,
- state.availableLocationIds.map {
- if (it == "") {
- getString(R.string.ipscrambling_any_location)
- } else {
- Locale("", it).displayCountry
- }
- }
- ).apply {
- setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View, position: Int, id: Long) {
+ viewModel.submitAction(InternetPrivacyFeature.Action.SelectLocationAction(position))
}
- setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View, position: Int, id: Long) {
- viewModel.submitAction(InternetPrivacyFeature.Action.SelectLocationAction(position))
- }
+ override fun onNothingSelected(parentView: AdapterView<*>?) {}
+ })
- override fun onNothingSelected(parentView: AdapterView<*>?) {}
- })
-
- setSelection(state.selectedLocationPosition)
- }
-
- it.findViewById<TextView>(R.id.ipscrambling_activated)?.apply {
- text = getString(
- if (state.isAllAppsScrambled) R.string.ipscrambling_all_apps_scrambled
- else R.string.ipscrambling_only_selected_apps_scrambled
- )
- }
+ setSelection(state.selectedLocationPosition)
+ }
- it.findViewById<RecyclerView>(R.id.recycler_view_scrambled)?.apply {
- (adapter as ToggleAppsAdapter?)?.dataSet = state.getScrambledApps()
- }
- it.findViewById<RecyclerView>(R.id.recycler_view_to_select)?.apply {
- (adapter as ToggleAppsAdapter?)?.dataSet = state.getApps()
- }
+ // TODO: this should not be mandatory.
+ binding.apps.post {
+ (binding.apps.adapter as ToggleAppsAdapter?)?.dataSet = state.getApps()
+ }
- val viewIdsToHide = listOf(
- R.id.ipscrambling_activated,
- R.id.recycler_view_scrambled,
- R.id.ipscrambling_select_apps,
- R.id.recycler_view_to_select,
- R.id.ipscrambling_location
+ val viewIdsToHide = listOf(
+ binding.ipscramblingLocationLabel,
+ binding.selectLocationContainer,
+ binding.ipscramblingSelectLocation,
+ binding.ipscramblingSelectApps,
+ binding.apps
+ )
+
+ when {
+ state.mode in listOf(
+ InternetPrivacyMode.HIDE_IP_LOADING,
+ InternetPrivacyMode.REAL_IP_LOADING
)
- val progressBar = it.findViewById<ProgressBar>(R.id.ipscrambling_loading)
-
- when {
- state.mode in listOf(
- InternetPrivacyMode.HIDE_IP_LOADING,
- InternetPrivacyMode.REAL_IP_LOADING
- )
- || state.availableApps.isEmpty() -> {
- progressBar?.visibility = View.VISIBLE
- viewIdsToHide.forEach { viewId -> it.findViewById<View>(viewId)?.visibility = View.GONE }
- }
- else -> {
- progressBar?.visibility = View.GONE
- viewIdsToHide.forEach { viewId -> it.findViewById<View>(viewId)?.visibility = View.VISIBLE }
- }
+ || state.availableApps.isEmpty() -> {
+ binding.loader.visibility = View.VISIBLE
+ viewIdsToHide.forEach { it.visibility = View.GONE }
+ }
+ else -> {
+ binding.loader.visibility = View.GONE
+ viewIdsToHide.forEach { it.visibility = View.VISIBLE }
}
}
}
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
index 6f3c200..2ffa92e 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt
@@ -20,19 +20,19 @@ package foundation.e.privacycentralapp.features.internetprivacy
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import foundation.e.privacycentralapp.common.Factory
+import foundation.e.privacycentralapp.domain.usecases.AppListUseCase
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
-import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
class InternetPrivacyViewModel(
private val ipScramblerModule: IIpScramblerModule,
- private val permissionsModule: PermissionsPrivacyModule,
private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
- private val ipScramblingStateUseCase: IpScramblingStateUseCase
+ private val ipScramblingStateUseCase: IpScramblingStateUseCase,
+ private val appListUseCase: AppListUseCase
) : ViewModel() {
private val _actions = MutableSharedFlow<InternetPrivacyFeature.Action>()
@@ -42,9 +42,9 @@ class InternetPrivacyViewModel(
InternetPrivacyFeature.create(
coroutineScope = viewModelScope,
ipScramblerModule = ipScramblerModule,
- permissionsModule = permissionsModule,
getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
- ipScramblingStateUseCase = ipScramblingStateUseCase
+ ipScramblingStateUseCase = ipScramblingStateUseCase,
+ appListUseCase = appListUseCase
)
}
@@ -57,12 +57,12 @@ class InternetPrivacyViewModel(
class InternetPrivacyViewModelFactory(
private val ipScramblerModule: IIpScramblerModule,
- private val permissionsModule: PermissionsPrivacyModule,
private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
- private val ipScramblingStateUseCase: IpScramblingStateUseCase
+ private val ipScramblingStateUseCase: IpScramblingStateUseCase,
+ private val appListUseCase: AppListUseCase
) :
Factory<InternetPrivacyViewModel> {
override fun create(): InternetPrivacyViewModel {
- return InternetPrivacyViewModel(ipScramblerModule, permissionsModule, getQuickPrivacyStateUseCase, ipScramblingStateUseCase)
+ return InternetPrivacyViewModel(ipScramblerModule, getQuickPrivacyStateUseCase, ipScramblingStateUseCase, appListUseCase)
}
}
diff --git a/app/src/main/res/drawable/ic_ic_chevron_left_24dp.xml b/app/src/main/res/drawable/ic_ic_chevron_left_24dp.xml
new file mode 100644
index 0000000..e88ab33
--- /dev/null
+++ b/app/src/main/res/drawable/ic_ic_chevron_left_24dp.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">
+ <path
+ android:pathData="M17.885,3.77L16.115,2L6.115,12L16.115,22L17.885,20.23L9.655,12L17.885,3.77Z"
+ android:fillColor="#000000"
+ android:fillAlpha="0.87"/>
+</vector>
diff --git a/app/src/main/res/drawable/outlined_background.xml b/app/src/main/res/drawable/outlined_background.xml
new file mode 100644
index 0000000..2040f18
--- /dev/null
+++ b/app/src/main/res/drawable/outlined_background.xml
@@ -0,0 +1,22 @@
+<?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/>.
+ -->
+
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke android:width="1dp" android:color="@color/grey_divider" />
+ <corners android:radius="4dp" />
+</shape>
diff --git a/app/src/main/res/layout/fragment_internet_activity_policy.xml b/app/src/main/res/layout/fragment_internet_activity_policy.xml
index 982b7c4..4f29776 100644
--- a/app/src/main/res/layout/fragment_internet_activity_policy.xml
+++ b/app/src/main/res/layout/fragment_internet_activity_policy.xml
@@ -24,9 +24,7 @@
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
- android:paddingLeft="32dp"
- android:paddingRight="32dp"
- tools:context=".main.MainActivity"
+ android:padding="16dp"
>
<TextView
@@ -34,33 +32,8 @@
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:paddingTop="16dp"
- android:text="@string/internet_activity_privacy_info"
- android:textColor="@color/black"
- android:textSize="14sp"
- />
-
- <TextView
- android:fontFamily="sans-serif-medium"
- android:gravity="center_vertical"
- android:id="@+id/learn_more_internet_activity_privacy_info"
- android:layout_height="48dp"
- android:layout_width="wrap_content"
- android:text="@string/learn_more"
- android:textColor="#007fff"
- android:textSize="14sp"
- />
-
- <TextView
- android:fontFamily="sans-serif-medium"
- android:id="@+id/my_internet_activity_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingBottom="8dp"
- android:paddingTop="16dp"
- android:text="@string/dashboard_internet_activity_privacy_title"
- android:textColor="@color/black"
- android:textSize="14sp"
+ android:text="@string/ipscrambling_info"
+ android:lineSpacingExtra="5sp"
/>
<RadioGroup
@@ -69,114 +42,82 @@
android:layout_width="match_parent"
android:orientation="vertical"
>
-
- <foundation.e.privacycentralapp.common.RightRadioButton
+ <include layout="@layout/ipscrambling_item_selectmode"
+ android:id="@+id/radio_use_hidden_ip"
+ android:layout_width="match_parent"
+ android:layout_height="88dp"
+ app:title="@{@string/ipscrambling_hide_ip_label}"
+ app:subtitle="@{@string/ipscrambling_hide_ip_subtitle}"
+ />
+ <include layout="@layout/ipscrambling_item_selectmode"
android:id="@+id/radio_use_real_ip"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:text="@string/use_real_ip"
- android:textSize="16sp"
- />
-
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="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_height="wrap_content"
- android:layout_marginTop="8dp"
android:layout_width="match_parent"
- android:text="@string/hidden_ip"
- android:textSize="16sp"
- />
-
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/i_am_anonymous"
- android:textSize="14sp"
+ android:layout_height="88dp"
+ app:title="@{@string/ipscrambling_real_ip_label}"
+ app:subtitle="@{@string/ipscrambling_real_ip_subtitle}"
/>
</RadioGroup>
- <TextView android:id="@+id/ipscrambling_tor_status"
+
+ <TextView android:id="@+id/tor_disclaimer"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:paddingTop="24dp"
- android:text="@string/ipscrambling_is_starting"
- android:textColor="@color/black"
- android:textSize="16sp"
- android:visibility="gone"
+ android:padding="4dp"
+ android:textSize="12sp"
+ android:textColor="@color/black_text_2"
+ android:text="@string/ipscrambling_tor_disclaimer"
+
/>
<ProgressBar
- android:id="@+id/ipscrambling_loading"
+ android:id="@+id/loader"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="24dp"
android:layout_gravity="center"
+ android:visibility="gone"
/>
- <LinearLayout
- android:id="@+id/ipscrambling_location"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="32dp"
- >
- <TextView
- android:id="@+id/ipscrambling_location_label"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" android:text="@string/ipscrambling_location_label"
- android:textColor="@color/black"
- android:textSize="16sp"
- />
- <Spinner android:id="@+id/ipscrambling_select_location"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- />
- </LinearLayout>
-
<TextView
- android:id="@+id/ipscrambling_activated"
+ android:id="@+id/ipscrambling_location_label"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:paddingTop="32dp"
- android:text="@string/ipscrambling_all_apps_scrambled"
- android:textColor="@color/black"
- android:paddingBottom="8dp"
- android:textSize="16sp"
- android:visibility="gone"
+ android:layout_marginTop="16dp"
+ android:text="@string/ipscrambling_location_label"
+ android:textColor="@color/black_text"
+ android:textFontWeight="500"
+ android:textSize="14sp"
/>
- </LinearLayout>
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/recycler_view_scrambled"
+ <FrameLayout
+ android:id="@+id/select_location_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
+ android:layout_height="54dp"
+ android:background="@drawable/outlined_background"
+ android:layout_marginTop="16dp"
+ android:padding="1dp"
+ >
+ <Spinner android:id="@+id/ipscrambling_select_location"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ />
+ </FrameLayout>
<TextView
android:id="@+id/ipscrambling_select_apps"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:paddingStart="32dp"
- android:paddingEnd="32dp"
- android:paddingTop="32dp"
- android:paddingBottom="8dp"
+ android:layout_marginTop="32dp"
+ android:layout_marginBottom="16dp"
android:text="@string/ipscrambling_select_app"
- android:textColor="@color/black"
- android:textSize="16sp"
- android:visibility="gone"
+ android:textColor="@color/black_text"
+ android:textFontWeight="500"
+ android:textSize="14sp"
/>
+ </LinearLayout>
+
<androidx.recyclerview.widget.RecyclerView
- android:id="@+id/recycler_view_to_select"
+ android:id="@+id/apps"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/app/src/main/res/layout/ipscrambling_item_app_toggle.xml b/app/src/main/res/layout/ipscrambling_item_app_toggle.xml
new file mode 100644
index 0000000..117b834
--- /dev/null
+++ b/app/src/main/res/layout/ipscrambling_item_app_toggle.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_height="52dp"
+ android:layout_width="match_parent"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical"
+ >
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_height="32dp"
+ android:layout_width="32dp"
+ android:src="@drawable/ic_facebook"
+ />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_centerVertical="true"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_marginStart="16dp"
+ android:textSize="14sp"
+ tools:text="Body sensor"
+ />
+
+ <CheckBox
+ android:id="@+id/toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="24dp"
+ android:layoutDirection="rtl"
+ android:checked="true"
+ />
+</androidx.appcompat.widget.LinearLayoutCompat>
diff --git a/app/src/main/res/layout/ipscrambling_item_selectmode.xml b/app/src/main/res/layout/ipscrambling_item_selectmode.xml
new file mode 100644
index 0000000..4758d56
--- /dev/null
+++ b/app/src/main/res/layout/ipscrambling_item_selectmode.xml
@@ -0,0 +1,57 @@
+<?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/>.
+ -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ >
+ <data>
+ <variable name="title" type="String"/>
+ <variable name="subtitle" type="String"/>
+ </data>
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="88dp"
+ >
+ <foundation.e.privacycentralapp.common.RightRadioButton
+ android:id="@+id/radiobutton"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ />
+ <TextView
+ android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@{title}"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/subtitle"
+ app:layout_constraintVertical_chainStyle="packed"
+ />
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@{subtitle}"
+ android:textSize="14sp"
+ android:textColor="@color/grey_text"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/title"
+ app:layout_constraintBottom_toBottomOf="parent"
+ />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 2a16240..5f4bcb4 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -11,6 +11,7 @@
<color name="accent">@lineageos.platform:color/color_default_accent</color>
<color name="black_text">#DE000000</color>
+ <color name="black_text_2">#BD000000</color>
<color name="grey_text">#99000000</color>
<color name="grey_text_2">#61000000</color>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f6bcf72..35987ea 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -30,8 +30,20 @@
<string name="dashboard_internet_activity_privacy_subtitle_off">Real IP address exposed</string>
<string name="dashboard_internet_activity_privacy_subtitle_on">Real IP address hidden</string>
+ <!-- IpScrambling -->
+ <string name="ipscrambling_title">My internet activity</string>
+ <string name="ipscrambling_info">Choose if you want to expose your real IP address or hide when Quick Privacy is enabled.</string>
+ <string name="ipscrambling_real_ip_label">Use real IP address</string>
+ <string name="ipscrambling_real_ip_subtitle">I can be tracked by my IP address</string>
+ <string name="ipscrambling_hide_ip_label">Hide IP address</string>
+ <string name="ipscrambling_hide_ip_subtitle">I am anonymous on the internet</string>
+ <string name="ipscrambling_tor_disclaimer"><b>Note:</b> when active, this setting will slow down your Internet connectivity speed (uses Thor network).</string>
+ <string name="ipscrambling_location_label">Force a country of origin:</string>
+ <string name="ipscrambling_any_location">Any country</string>
+
+ <string name="ipscrambling_select_app">Apply this setting to all selected apps:</string>
+
<!-- -->
- <string name="internet_activity_privacy">Internet Activity Privacy</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>
@@ -42,19 +54,12 @@
<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="ipscrambling_all_apps_scrambled">All apps use hidden IP</string>
<string name="ipscrambling_only_selected_apps_scrambled">Only the following apps use hidden IP</string>
- <string name="ipscrambling_select_app">Select Apps to hide IP</string>
+
<string name="ipscrambling_is_starting">Tor is starting...</string>
<string name="ipscrambling_is_stopping">Tor is stopping...</string>
- <string name="ipscrambling_location_label">Hidden IP\'s location</string>
- <string name="ipscrambling_any_location">any</string>
ipscrambling_any_location
<string name="permission_control_info">Manage and control apps requesting various permissions.</string>