summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt6
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/Notifications.kt26
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/common/NavToolbarFragment.kt37
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/common/ToolbarFragment.kt45
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt6
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt3
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt69
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardViewModel.kt25
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt3
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt3
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt32
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt11
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt26
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/main/MainActivity.kt84
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt20
-rw-r--r--app/src/main/res/layout/activity_main.xml27
-rw-r--r--app/src/main/res/navigation/nav_graph.xml91
17 files changed, 227 insertions, 287 deletions
diff --git a/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt b/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt
index 91e2f44..5664515 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt
@@ -41,7 +41,7 @@ import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel
import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyViewModel
import foundation.e.advancedprivacy.features.location.FakeLocationViewModel
import foundation.e.advancedprivacy.features.trackers.TrackersViewModel
-import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersFragment
+import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersFragmentArgs
import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersViewModel
import foundation.e.privacymodules.fakelocation.FakeLocationModule
import foundation.e.privacymodules.ipscrambler.IpScramblerModule
@@ -172,8 +172,8 @@ class ViewModelsFactory(
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
return when (modelClass) {
AppTrackersViewModel::class.java -> {
- val app = extras[DEFAULT_ARGS_KEY]?.getInt(AppTrackersFragment.PARAM_APP_UID)?.let {
- appListUseCase.getApp(it)
+ val app = extras[DEFAULT_ARGS_KEY]?.let {
+ appListUseCase.getApp(AppTrackersFragmentArgs.fromBundle(it).appUid)
} ?: appListUseCase.dummySystemApp
AppTrackersViewModel(
diff --git a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
index 68c4bd3..291f9bc 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2022 MURENA SAS
*
* This program is free software: you can redistribute it and/or modify
@@ -21,7 +22,6 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
-import android.content.Intent
import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@@ -54,8 +54,9 @@ object Notifications {
icon = R.drawable.ic_notification_logo,
title = R.string.first_notification_title,
description = R.string.first_notification_summary,
- destinationIntent =
- context.packageManager.getLaunchIntentForPackage(context.packageName)
+ pendingIntent = MainActivity.deepLinkBuilder(context)
+ .setDestination(R.id.dashboardFragment)
+ .createPendingIntent()
)
)
.setAutoCancel(true)
@@ -140,7 +141,9 @@ object Notifications {
icon = R.drawable.ic_fmd_bad,
title = R.string.notifications_fake_location_title,
description = R.string.notifications_fake_location_content,
- destinationIntent = MainActivity.createFakeLocationIntent(context),
+ pendingIntent = MainActivity.deepLinkBuilder(context)
+ .addDestination(R.id.fakeLocationFragment)
+ .createPendingIntent()
)
)
MainFeatures.IP_SCRAMBLING -> showFlagNotification(
@@ -151,7 +154,9 @@ object Notifications {
icon = R.drawable.ic_language,
title = R.string.notifications_ipscrambling_title,
description = R.string.notifications_ipscrambling_content,
- destinationIntent = MainActivity.createIpScramblingIntent(context),
+ pendingIntent = MainActivity.deepLinkBuilder(context)
+ .addDestination(R.id.internetPrivacyFragment)
+ .createPendingIntent()
)
)
else -> {}
@@ -184,7 +189,7 @@ object Notifications {
val icon: Int,
val title: Int,
val description: Int,
- val destinationIntent: Intent?
+ val pendingIntent: PendingIntent?
)
private fun notificationBuilder(
@@ -196,14 +201,7 @@ object Notifications {
.setPriority(NotificationCompat.PRIORITY_LOW)
.setContentTitle(context.getString(content.title))
.setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(content.description)))
-
- content.destinationIntent?.let {
- it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- val pendingIntent: PendingIntent = PendingIntent.getActivity(
- context, 0, it, PendingIntent.FLAG_IMMUTABLE
- )
- builder.setContentIntent(pendingIntent)
- }
+ content.pendingIntent?.let { builder.setContentIntent(it) }
return builder
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/NavToolbarFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/common/NavToolbarFragment.kt
index 1417977..cdb6a4c 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/common/NavToolbarFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/NavToolbarFragment.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -17,17 +18,39 @@
package foundation.e.advancedprivacy.common
+import android.os.Bundle
+import android.view.View
import androidx.annotation.LayoutRes
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
import com.google.android.material.appbar.MaterialToolbar
+import foundation.e.advancedprivacy.R
-abstract class NavToolbarFragment(@LayoutRes contentLayoutId: Int) : ToolbarFragment(contentLayoutId) {
+abstract class NavToolbarFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId) {
- override fun setupToolbar(toolbar: MaterialToolbar) {
- super.setupToolbar(toolbar)
- toolbar.apply {
- setNavigationOnClickListener {
- requireActivity().onBackPressed()
- }
+ /**
+ * @return title to be used in toolbar
+ */
+ open fun getTitle(): CharSequence {
+ return findNavController().currentDestination?.label ?: ""
+ }
+
+ fun setTitle(title: CharSequence?) {
+ getToolbar()?.title = title
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupToolbar(view.findViewById(R.id.toolbar))
+ }
+
+ open fun setupToolbar(toolbar: MaterialToolbar) {
+ toolbar.title = getTitle()
+ toolbar.setNavigationOnClickListener {
+ requireActivity().onBackPressed()
}
}
+
+ fun getToolbar(): MaterialToolbar? = view?.findViewById(R.id.toolbar)
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/ToolbarFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/common/ToolbarFragment.kt
deleted file mode 100644
index fb3ea14..0000000
--- a/app/src/main/java/foundation/e/advancedprivacy/common/ToolbarFragment.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.advancedprivacy.common
-
-import android.os.Bundle
-import android.view.View
-import androidx.annotation.LayoutRes
-import androidx.fragment.app.Fragment
-import com.google.android.material.appbar.MaterialToolbar
-import foundation.e.advancedprivacy.R
-
-abstract class ToolbarFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId) {
-
- /**
- * @return title to be used in toolbar
- */
- abstract fun getTitle(): String
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- setupToolbar(view.findViewById(R.id.toolbar))
- }
-
- open fun setupToolbar(toolbar: MaterialToolbar) {
- toolbar.title = getTitle()
- }
-
- fun getToolbar(): MaterialToolbar? = view?.findViewById(R.id.toolbar)
-}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
index 98deeb1..3f3f66c 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2022 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -120,7 +121,10 @@ class WarningDialog : Activity() {
if (feature == TRACKERS_CONTROL) {
builder.setNeutralButton(R.string.warningdialog_trackers_secondary_cta) { _, _ ->
- startActivity(MainActivity.createTrackersIntent(this))
+ MainActivity.deepLinkBuilder(this)
+ .setDestination(R.id.trackersFragment)
+ .createPendingIntent().send()
+
finish()
}
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
index 9b99b95..5a16308 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -144,7 +145,7 @@ class FakeLocationStateUseCase(
}
}
- // Deprecated since API 29, never called.
+ @Deprecated("Deprecated since API 29, never called.")
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
override fun onProviderEnabled(provider: String) {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
index 02fd1ad..6ca9792 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -17,21 +18,19 @@
package foundation.e.advancedprivacy.features.dashboard
-import android.content.Intent
import android.os.Bundle
import android.text.Html
import android.text.Html.FROM_HTML_MODE_LEGACY
import android.view.View
import android.widget.Toast
import androidx.core.content.ContextCompat.getColor
-import androidx.core.os.bundleOf
import androidx.core.view.isVisible
-import androidx.fragment.app.commit
-import androidx.fragment.app.replace
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
import foundation.e.advancedprivacy.AdvancedPrivacyApplication
import foundation.e.advancedprivacy.DependencyContainer
import foundation.e.advancedprivacy.R
@@ -44,20 +43,9 @@ import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
import foundation.e.advancedprivacy.domain.entities.TrackerMode
import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel.Action
import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel.SingleEvent
-import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyFragment
-import foundation.e.advancedprivacy.features.location.FakeLocationFragment
-import foundation.e.advancedprivacy.features.trackers.TrackersFragment
-import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersFragment
import kotlinx.coroutines.launch
class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
- companion object {
- private const val PARAM_HIGHLIGHT_INDEX = "PARAM_HIGHLIGHT_INDEX"
- fun buildArgs(highlightIndex: Int): Bundle = bundleOf(
- PARAM_HIGHLIGHT_INDEX to highlightIndex
- )
- }
-
private val dependencyContainer: DependencyContainer by lazy {
(this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer
}
@@ -73,10 +61,11 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
private var highlightIndexOnStart: Int? = null
+ private val args: DashboardFragmentArgs by navArgs()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- highlightIndexOnStart = arguments?.getInt(PARAM_HIGHLIGHT_INDEX, -1)
+ highlightIndexOnStart = args.highlightLeaks
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -134,45 +123,6 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.singleEvents.collect { event ->
when (event) {
- is SingleEvent.NavigateToLocationSingleEvent -> {
- requireActivity().supportFragmentManager.commit {
- replace<FakeLocationFragment>(R.id.container)
- setReorderingAllowed(true)
- addToBackStack("dashboard")
- }
- }
- is SingleEvent.NavigateToInternetActivityPrivacySingleEvent -> {
- requireActivity().supportFragmentManager.commit {
- replace<InternetPrivacyFragment>(R.id.container)
- setReorderingAllowed(true)
- addToBackStack("dashboard")
- }
- }
- is SingleEvent.NavigateToPermissionsSingleEvent -> {
- val intent = Intent("android.intent.action.MANAGE_PERMISSIONS")
- requireActivity().startActivity(intent)
- }
- SingleEvent.NavigateToTrackersSingleEvent -> {
- requireActivity().supportFragmentManager.commit {
- replace<TrackersFragment>(R.id.container)
- setReorderingAllowed(true)
- addToBackStack("dashboard")
- }
- }
- is SingleEvent.NavigateToAppDetailsEvent -> {
- requireActivity().supportFragmentManager.commit {
- replace<AppTrackersFragment>(
- R.id.container,
- args = AppTrackersFragment.buildArgs(
- event.appDesc.label.toString(),
- event.appDesc.packageName,
- event.appDesc.uid
- )
- )
- setReorderingAllowed(true)
- addToBackStack("dashboard")
- }
- }
is SingleEvent.ToastMessageSingleEvent ->
Toast.makeText(
requireContext(),
@@ -183,6 +133,11 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
}
}
}
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.navigate.collect(findNavController()::navigate)
+ }
+ }
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -191,10 +146,6 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
}
}
- override fun getTitle(): String {
- return getString(R.string.dashboard_title)
- }
-
private fun render(state: DashboardState) {
binding.stateLabel.text = getString(
when (state.quickPrivacyState) {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardViewModel.kt
index cc1263b..8259c89 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardViewModel.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardViewModel.kt
@@ -1,5 +1,5 @@
/*
-* Copyright (C) 2023 MURENA SAS
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -21,10 +21,10 @@ package foundation.e.advancedprivacy.features.dashboard
import androidx.annotation.StringRes
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import androidx.navigation.NavDirections
import foundation.e.advancedprivacy.R
import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@@ -51,6 +51,8 @@ class DashboardViewModel(
private val _singleEvents = MutableSharedFlow<SingleEvent>()
val singleEvents = _singleEvents.asSharedFlow()
+ private val _navigate = MutableSharedFlow<NavDirections>()
+ val navigate = _navigate.asSharedFlow()
init {
viewModelScope.launch(Dispatchers.IO) { trackersStatisticsUseCase.initAppList() }
}
@@ -98,13 +100,13 @@ class DashboardViewModel(
is Action.ToggleIpScrambling ->
getPrivacyStateUseCase.toggleIpScrambling(action.enabled)
is Action.ShowFakeMyLocationAction ->
- _singleEvents.emit(SingleEvent.NavigateToLocationSingleEvent)
+ _navigate.emit(DashboardFragmentDirections.gotoFakeLocationFragment())
is Action.ShowAppsPermissions ->
- _singleEvents.emit(SingleEvent.NavigateToPermissionsSingleEvent)
+ _navigate.emit(DashboardFragmentDirections.gotoSettingsPermissionsActivity())
is Action.ShowInternetActivityPrivacyAction ->
- _singleEvents.emit(SingleEvent.NavigateToInternetActivityPrivacySingleEvent)
+ _navigate.emit(DashboardFragmentDirections.gotoInternetPrivacyFragment())
is Action.ShowTrackers ->
- _singleEvents.emit(SingleEvent.NavigateToTrackersSingleEvent)
+ _navigate.emit(DashboardFragmentDirections.gotoTrackersFragment())
is Action.ShowMostLeakedApp -> actionShowMostLeakedApp()
}
}
@@ -127,19 +129,14 @@ class DashboardViewModel(
}
private suspend fun actionShowMostLeakedApp() = withContext(Dispatchers.IO) {
- _singleEvents.emit(
+ _navigate.emit(
trackersStatisticsUseCase.getMostLeakedApp()?.let {
- SingleEvent.NavigateToAppDetailsEvent(appDesc = it)
- } ?: SingleEvent.NavigateToTrackersSingleEvent
+ DashboardFragmentDirections.gotoAppTrackersFragment(appUid = it.uid)
+ } ?: DashboardFragmentDirections.gotoTrackersFragment()
)
}
sealed class SingleEvent {
- object NavigateToTrackersSingleEvent : SingleEvent()
- object NavigateToInternetActivityPrivacySingleEvent : SingleEvent()
- object NavigateToLocationSingleEvent : SingleEvent()
- object NavigateToPermissionsSingleEvent : SingleEvent()
- data class NavigateToAppDetailsEvent(val appDesc: ApplicationDescription) : SingleEvent()
data class ToastMessageSingleEvent(
@StringRes val message: Int,
val args: List<Any> = emptyList()
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
index 07da82a..35fc1d4 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -141,8 +142,6 @@ class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_ac
}
}
- override fun getTitle(): String = getString(R.string.ipscrambling_title)
-
private fun render(state: InternetPrivacyState) {
binding.radioUseHiddenIp.radiobutton.apply {
isChecked = state.mode in listOf(
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt
index 9934713..09409f2 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -100,8 +101,6 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location)
Mapbox.getInstance(requireContext(), getString(R.string.mapbox_key), WellKnownTileServer.Mapbox)
}
- override fun getTitle(): String = getString(R.string.location_title)
-
private fun displayToast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
.show()
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
index 3e17334..f486114 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2021 E FOUNDATION, 2022 MURENA SAS
+ * Copyright (C) 2022-2023 MURENA SAS
+ * 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
@@ -30,12 +31,11 @@ import android.view.View
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
-import androidx.fragment.app.commit
-import androidx.fragment.app.replace
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import foundation.e.advancedprivacy.AdvancedPrivacyApplication
import foundation.e.advancedprivacy.DependencyContainer
@@ -47,11 +47,9 @@ import foundation.e.advancedprivacy.common.setToolTipForAsterisk
import foundation.e.advancedprivacy.databinding.FragmentTrackersBinding
import foundation.e.advancedprivacy.databinding.TrackersItemGraphBinding
import foundation.e.advancedprivacy.domain.entities.TrackersPeriodicStatistics
-import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersFragment
import kotlinx.coroutines.launch
-class TrackersFragment :
- NavToolbarFragment(R.layout.fragment_trackers) {
+class TrackersFragment : NavToolbarFragment(R.layout.fragment_trackers) {
private val dependencyContainer: DependencyContainer by lazy {
(this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer
@@ -134,20 +132,6 @@ class TrackersFragment :
is TrackersViewModel.SingleEvent.ErrorEvent -> {
displayToast(event.error)
}
- is TrackersViewModel.SingleEvent.OpenAppDetailsEvent -> {
- requireActivity().supportFragmentManager.commit {
- replace<AppTrackersFragment>(
- R.id.container,
- args = AppTrackersFragment.buildArgs(
- event.appDesc.label.toString(),
- event.appDesc.packageName,
- event.appDesc.uid
- )
- )
- setReorderingAllowed(true)
- addToBackStack("apptrackers")
- }
- }
is TrackersViewModel.SingleEvent.OpenUrl -> {
try {
startActivity(Intent(Intent.ACTION_VIEW, event.url))
@@ -166,6 +150,12 @@ class TrackersFragment :
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.navigate.collect(findNavController()::navigate)
+ }
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.doOnStartedState()
}
}
@@ -176,8 +166,6 @@ class TrackersFragment :
.show()
}
- override fun getTitle() = getString(R.string.trackers_title)
-
private fun render(state: TrackersState) {
state.dayStatistics?.let { renderGraph(it, dayGraphHolder!!, binding.graphDay) }
state.monthStatistics?.let { renderGraph(it, monthGraphHolder!!, binding.graphMonth) }
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt
index bcb4df8..8a5d0f0 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2021 E FOUNDATION, 2022 MURENA SAS
+ * Copyright (C) 2022-2023 MURENA SAS
+ * 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
@@ -20,7 +21,7 @@ package foundation.e.advancedprivacy.features.trackers
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import foundation.e.advancedprivacy.domain.entities.AppWithCounts
+import androidx.navigation.NavDirections
import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -48,6 +49,9 @@ class TrackersViewModel(
private val _singleEvents = MutableSharedFlow<SingleEvent>()
val singleEvents = _singleEvents.asSharedFlow()
+ private val _navigate = MutableSharedFlow<NavDirections>()
+ val navigate = _navigate.asSharedFlow()
+
suspend fun doOnStartedState() = withContext(Dispatchers.IO) {
merge(
trackersStatisticsUseCase.listenUpdates().map {
@@ -78,13 +82,12 @@ class TrackersViewModel(
private suspend fun actionClickApp(action: Action.ClickAppAction) {
state.value.apps?.find { it.uid == action.appUid }?.let {
- _singleEvents.emit(SingleEvent.OpenAppDetailsEvent(it))
+ _navigate.emit(TrackersFragmentDirections.gotoAppTrackersFragment(appUid = it.uid))
}
}
sealed class SingleEvent {
data class ErrorEvent(val error: String) : SingleEvent()
- data class OpenAppDetailsEvent(val appDesc: AppWithCounts) : SingleEvent()
data class OpenUrl(val url: Uri) : SingleEvent()
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt
index 2bb53d6..457a02a 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt
@@ -23,7 +23,6 @@ import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
-import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
@@ -39,19 +38,6 @@ import foundation.e.advancedprivacy.databinding.ApptrackersFragmentBinding
import kotlinx.coroutines.launch
class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) {
- companion object {
- private val PARAM_LABEL = "PARAM_LABEL"
- private val PARAM_PACKAGE_NAME = "PARAM_PACKAGE_NAME"
-
- const val PARAM_APP_UID = "PARAM_APP_UID"
-
- fun buildArgs(label: String, packageName: String, appUid: Int): Bundle = bundleOf(
- PARAM_LABEL to label,
- PARAM_PACKAGE_NAME to packageName,
- PARAM_APP_UID to appUid
- )
- }
-
private val dependencyContainer: DependencyContainer by lazy {
(this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer
}
@@ -63,13 +49,8 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) {
private var _binding: ApptrackersFragmentBinding? = null
private val binding get() = _binding!!
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- if (arguments == null ||
- requireArguments().getInt(PARAM_APP_UID, Int.MIN_VALUE) == Int.MIN_VALUE
- ) {
- activity?.supportFragmentManager?.popBackStack()
- }
+ override fun getTitle(): CharSequence {
+ return ""
}
private fun displayToast(message: String) {
@@ -77,8 +58,6 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) {
.show()
}
- override fun getTitle(): String = requireArguments().getString(PARAM_LABEL) ?: ""
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = ApptrackersFragmentBinding.bind(view)
@@ -144,6 +123,7 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) {
}
private fun render(state: AppTrackersState) {
+ setTitle(state.appDesc?.label)
binding.trackersCountSummary.text = if (state.getTrackersCount() == 0) ""
else getString(
R.string.apptrackers_trackers_count_summary,
diff --git a/app/src/main/java/foundation/e/advancedprivacy/main/MainActivity.kt b/app/src/main/java/foundation/e/advancedprivacy/main/MainActivity.kt
index 277031a..fbe5cf8 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/main/MainActivity.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/main/MainActivity.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -17,91 +18,22 @@
package foundation.e.advancedprivacy.main
-import android.app.Activity
import android.content.Context
import android.content.Intent
-import android.os.Bundle
-import android.util.Log
import androidx.fragment.app.FragmentActivity
-import androidx.fragment.app.add
-import androidx.fragment.app.commit
+import androidx.navigation.NavDeepLinkBuilder
+import androidx.navigation.findNavController
import foundation.e.advancedprivacy.R
-import foundation.e.advancedprivacy.features.dashboard.DashboardFragment
-import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyFragment
-import foundation.e.advancedprivacy.features.location.FakeLocationFragment
-import foundation.e.advancedprivacy.features.trackers.TrackersFragment
-
-open class MainActivity : FragmentActivity(R.layout.activity_main) {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- if (savedInstanceState == null) handleIntent(intent)
- }
+class MainActivity : FragmentActivity(R.layout.activity_main) {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
- handleIntent(intent)
- }
-
- open fun handleIntent(intent: Intent) {
- supportFragmentManager.commit {
- setReorderingAllowed(true)
- when (intent.action) {
- ACTION_HIGHLIGHT_LEAKS -> add<DashboardFragment>(
- containerViewId = R.id.container,
- args = intent.extras
- )
- ACTION_VIEW_TRACKERS -> {
- add<TrackersFragment>(R.id.container)
- }
- ACTION_VIEW_FAKE_LOCATION -> {
- add<FakeLocationFragment>(R.id.container)
- }
- ACTION_VIEW_IPSCRAMBLING -> {
- add<InternetPrivacyFragment>(R.id.container)
- }
- else -> add<DashboardFragment>(R.id.container)
- }
- disallowAddToBackStack()
- }
- }
-
- override fun finishAfterTransition() {
- val resultData = Intent()
- val result = onPopulateResultIntent(resultData)
- setResult(result, resultData)
-
- super.finishAfterTransition()
+ findNavController(R.id.nav_host_fragment).handleDeepLink(intent)
}
- open fun onPopulateResultIntent(intent: Intent): Int = Activity.RESULT_OK
-
companion object {
- private const val ACTION_HIGHLIGHT_LEAKS = "ACTION_HIGHLIGHT_LEAKS"
- private const val ACTION_VIEW_TRACKERS = "ACTION_VIEW_TRACKERS"
- private const val ACTION_VIEW_FAKE_LOCATION = "ACTION_VIEW_FAKE_LOCATION"
- private const val ACTION_VIEW_IPSCRAMBLING = "ACTION_VIEW_IPSCRAMBLING"
-
- fun createHighlightLeaksIntent(context: Context, highlightIndex: Int) =
- Intent(context, MainActivity::class.java).apply {
- action = ACTION_HIGHLIGHT_LEAKS
- putExtras(DashboardFragment.buildArgs(highlightIndex))
- }
-
- fun createTrackersIntent(context: Context) =
- Intent(context, MainActivity::class.java).apply {
- action = ACTION_VIEW_TRACKERS
- }
-
- fun createFakeLocationIntent(context: Context): Intent {
- return Intent(context, MainActivity::class.java).apply {
- action = ACTION_VIEW_FAKE_LOCATION
- }
- }
-
- fun createIpScramblingIntent(context: Context): Intent {
- return Intent(context, MainActivity::class.java).apply {
- action = ACTION_VIEW_IPSCRAMBLING
- }
- }
+ fun deepLinkBuilder(context: Context) = NavDeepLinkBuilder(context)
+ .setGraph(R.navigation.nav_graph)
+ .setComponentName(MainActivity::class.java)
}
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
index f1edb36..bfd7d1a 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2022 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -33,6 +34,7 @@ import foundation.e.advancedprivacy.common.extensions.dpToPxF
import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
import foundation.e.advancedprivacy.domain.entities.TrackerMode
+import foundation.e.advancedprivacy.features.dashboard.DashboardFragmentArgs
import foundation.e.advancedprivacy.main.MainActivity
import foundation.e.advancedprivacy.widget.WidgetCommandReceiver.Companion.ACTION_TOGGLE_IPSCRAMBLING
import foundation.e.advancedprivacy.widget.WidgetCommandReceiver.Companion.ACTION_TOGGLE_LOCATION
@@ -179,12 +181,9 @@ fun render(
setViewVisibility(R.id.graph_legend_values, View.VISIBLE)
setViewVisibility(R.id.graph_view_trackers_btn, View.VISIBLE)
- val pIntent = PendingIntent.getActivity(
- context,
- REQUEST_CODE_TRACKERS,
- MainActivity.createTrackersIntent(context),
- FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
- )
+ val pIntent = MainActivity.deepLinkBuilder(context)
+ .setDestination(R.id.trackersFragment)
+ .createPendingIntent()
setOnClickPendingIntent(R.id.graph_view_trackers_btn, pIntent)
@@ -205,11 +204,10 @@ fun render(
val topPadding = graphHeightPx - (blocked + leaked) * ratio
setViewPadding(leakedBarIds[index], 0, topPadding.toInt(), 0, 0)
- val highlightPIntent = PendingIntent.getActivity(
- context, REQUEST_CODE_HIGHLIGHT + index,
- MainActivity.createHighlightLeaksIntent(context, index),
- FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
- )
+ val highlightPIntent = MainActivity.deepLinkBuilder(context)
+ .setDestination(R.id.dashboardFragment)
+ .setArguments(DashboardFragmentArgs(highlightLeaks = index).toBundle())
+ .createPendingIntent()
setOnClickPendingIntent(containerBarIds[index], highlightPIntent)
}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 2627a32..a5816f4 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,6 +1,27 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 MURENA SAS
+ ~ 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/>.
+ -->
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/container"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/nav_host_fragment"
+ android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_height="match_parent"
android:layout_width="match_parent"
- /> \ No newline at end of file
+
+ app:defaultNavHost="true"
+ app:navGraph="@navigation/nav_graph"
+ />
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 0000000..52a1677
--- /dev/null
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 MURENA SAS
+ ~
+ ~ 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/>.
+ -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/nav_graph"
+ app:startDestination="@id/dashboardFragment"
+ >
+
+ <fragment
+ android:id="@+id/dashboardFragment"
+ android:name="foundation.e.advancedprivacy.features.dashboard.DashboardFragment"
+ android:label="@string/app_name"
+ >
+ <action
+ android:id="@+id/goto_trackersFragment"
+ app:destination="@id/trackersFragment"
+ />
+ <action
+ android:id="@+id/goto_fakeLocationFragment"
+ app:destination="@id/fakeLocationFragment"
+ />
+ <action
+ android:id="@+id/goto_internetPrivacyFragment"
+ app:destination="@id/internetPrivacyFragment"
+ />
+ <action
+ android:id="@+id/goto_appTrackersFragment"
+ app:destination="@id/appTrackersFragment"
+ />
+ <action
+ android:id="@+id/goto_settingsPermissionsActivity"
+ app:destination="@id/settingsPermissionsActivity"
+ />
+ <argument
+ android:name="highlightLeaks"
+ app:argType="integer"
+ android:defaultValue="-1"
+ />
+ </fragment>
+ <fragment
+ android:id="@+id/trackersFragment"
+ android:name="foundation.e.advancedprivacy.features.trackers.TrackersFragment"
+ android:label="@string/trackers_title"
+ >
+ <action
+ android:id="@+id/goto_appTrackersFragment"
+ app:destination="@id/appTrackersFragment"
+ />
+ </fragment>
+ <fragment
+ android:id="@+id/appTrackersFragment"
+ android:name="foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersFragment"
+ android:label="AppTrackersFragment"
+ >
+ <argument
+ android:name="appUid"
+ app:argType="integer"
+ android:defaultValue="1000"
+ />
+ </fragment>
+ <fragment
+ android:id="@+id/fakeLocationFragment"
+ android:name="foundation.e.advancedprivacy.features.location.FakeLocationFragment"
+ android:label="@string/location_title"
+ />
+ <fragment
+ android:id="@+id/internetPrivacyFragment"
+ android:name="foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyFragment"
+ android:label="@string/ipscrambling_title"
+ />
+ <activity
+ android:id="@+id/settingsPermissionsActivity"
+ android:label="@string/dashboard_apps_permissions_title"
+ app:action="android.intent.action.MANAGE_PERMISSIONS"
+ />
+</navigation>