From 74b9860784913c097ae59e58b0958da7744ebc2e Mon Sep 17 00:00:00 2001
From: Guillaume Jacquart <guillaume.jacquart@hoodbrains.com>
Date: Fri, 9 Jun 2023 06:34:09 +0000
Subject: 1227: use navigation graph component, avoid view (fragments)
 duplications

---
 .../e/advancedprivacy/DependencyContainer.kt       |  6 +-
 .../foundation/e/advancedprivacy/Notifications.kt  | 26 ++++---
 .../e/advancedprivacy/common/NavToolbarFragment.kt | 37 ++++++++--
 .../e/advancedprivacy/common/ToolbarFragment.kt    | 45 ------------
 .../e/advancedprivacy/common/WarningDialog.kt      |  6 +-
 .../domain/usecases/FakeLocationStateUseCase.kt    |  3 +-
 .../features/dashboard/DashboardFragment.kt        | 69 +++---------------
 .../features/dashboard/DashboardViewModel.kt       | 25 +++----
 .../internetprivacy/InternetPrivacyFragment.kt     |  3 +-
 .../features/location/FakeLocationFragment.kt      |  3 +-
 .../features/trackers/TrackersFragment.kt          | 32 +++------
 .../features/trackers/TrackersViewModel.kt         | 11 +--
 .../trackers/apptrackers/AppTrackersFragment.kt    | 26 +------
 .../e/advancedprivacy/main/MainActivity.kt         | 84 +++-------------------
 .../e/advancedprivacy/widget/WidgetUI.kt           | 20 +++---
 15 files changed, 112 insertions(+), 284 deletions(-)
 delete mode 100644 app/src/main/java/foundation/e/advancedprivacy/common/ToolbarFragment.kt

(limited to 'app/src/main/java/foundation/e')

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))
@@ -164,6 +148,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)
             }
 
-- 
cgit v1.2.1