From ee7193998ed892f8e966ee348ed6b0bf1f5961e8 Mon Sep 17 00:00:00 2001
From: Stefan Zabka <h_dieter@mail.ru>
Date: Fri, 23 Aug 2019 14:26:17 +0000
Subject: [PATCH] Switch to Navigation component.

---
 app/build.gradle                              |  5 +++
 .../ccc/events/badge/card10/MainActivity.kt   | 17 ++++---
 .../badge/card10/common/ConnectionService.kt  |  2 -
 .../filetransfer/BatchTransferFragment.kt     | 18 ++------
 .../filetransfer/FileTransferFragment.kt      | 14 ++----
 .../card10/hatchery/AppDetailFragment.kt      | 27 ++++--------
 .../badge/card10/hatchery/AppListFragment.kt  | 14 ++----
 .../events/badge/card10/main/MainFragment.kt  | 41 +++++++----------
 .../events/badge/card10/mood/MoodFragment.kt  |  1 +
 app/src/main/res/layout/activity_main.xml     | 14 ++++--
 .../main/res/layout/disconnected_fragment.xml | 42 ++++++++++++++++++
 app/src/main/res/navigation/nav_graph.xml     | 44 +++++++++++++++++++
 build.gradle                                  |  5 +++
 13 files changed, 150 insertions(+), 94 deletions(-)
 create mode 100644 app/src/main/res/layout/disconnected_fragment.xml
 create mode 100644 app/src/main/res/navigation/nav_graph.xml

diff --git a/app/build.gradle b/app/build.gradle
index 1af9535..60adf86 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -4,6 +4,8 @@ apply plugin: 'kotlin-android'
 
 apply plugin: 'kotlin-android-extensions'
 
+apply plugin: 'androidx.navigation.safeargs.kotlin'
+
 android {
     compileSdkVersion 29
     buildToolsVersion "29.0.1"
@@ -40,4 +42,7 @@ dependencies {
     implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
     implementation 'androidx.work:work-runtime-ktx:2.2.0'
     implementation 'org.apache.commons:commons-compress:1.18'
+    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
+    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
+
 }
diff --git a/app/src/main/java/de/ccc/events/badge/card10/MainActivity.kt b/app/src/main/java/de/ccc/events/badge/card10/MainActivity.kt
index 7adb206..64f9ef1 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/MainActivity.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/MainActivity.kt
@@ -31,6 +31,8 @@ import android.os.Bundle
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
+import androidx.navigation.NavController
+import androidx.navigation.findNavController
 import androidx.work.ExistingPeriodicWorkPolicy
 import androidx.work.PeriodicWorkRequestBuilder
 import androidx.work.WorkManager
@@ -95,15 +97,6 @@ class MainActivity : AppCompatActivity() {
     }
 
     fun permissionGranted() {
-        val fragment = when {
-            intent.action == "application/x.card10.app" -> InstallerFragment()
-            else -> MainFragment()
-        }
-
-        supportFragmentManager.beginTransaction()
-            .replace(R.id.fragment_container, fragment)
-            .commit()
-
 //        val workManager = WorkManager.getInstance(this)
 //        if (!bluetoothAdapter.bondedDevices.isEmpty()
 //        ) {
@@ -116,4 +109,10 @@ class MainActivity : AppCompatActivity() {
 //            workManager.cancelUniqueWork(WORK_NAME)
 //        }
     }
+
+    override fun onBackPressed() {
+        if (!findNavController(R.id.nav_host_fragment).popBackStack()) {
+            super.onBackPressed()
+        }
+    }
 }
diff --git a/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt b/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt
index 011096c..6215005 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt
@@ -31,8 +31,6 @@ import de.ccc.events.badge.card10.FILE_SERVICE_UUID
 import de.ccc.events.badge.card10.R
 import de.ccc.events.badge.card10.filetransfer.LowEffortService
 import de.ccc.events.badge.card10.time.Card10Service
-import java.lang.IllegalStateException
-import java.lang.NullPointerException
 import java.util.*
 
 private const val TAG = "ConnectionService"
diff --git a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt
index 801b7ca..16d8eab 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt
@@ -22,21 +22,17 @@
 
 package de.ccc.events.badge.card10.filetransfer
 
-import android.bluetooth.BluetoothGatt
-import android.net.Uri
 import android.os.Bundle
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
 import de.ccc.events.badge.card10.R
 import de.ccc.events.badge.card10.common.ConnectionService
 import de.ccc.events.badge.card10.common.GattListener
-import de.ccc.events.badge.card10.main.MainFragment
 import kotlinx.android.synthetic.main.batch_transfer_fragment.*
-import java.lang.Exception
-import java.lang.IllegalStateException
 
 private const val TAG = "BatchTransferFragment"
 
@@ -47,9 +43,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
-        val args = arguments ?: throw IllegalStateException()
-        val jobs = args.get("jobs") as? Array<TransferJob> ?: throw IllegalStateException()
+        val jobs = BatchTransferFragmentArgs.fromBundle(requireArguments()).jobs
         queue = TransferQueue(jobs)
     }
 
@@ -64,11 +58,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
         }
 
         button_done.setOnClickListener {
-            val fragment = MainFragment()
-            fragmentManager!!.beginTransaction()
-                .replace(R.id.fragment_container, fragment)
-                .addToBackStack(null)
-                .commit()
+            findNavController().popBackStack()
         }
 
         startTransfer()
@@ -108,7 +98,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
             val ctx = activity ?: throw IllegalStateException()
             val reader = ChunkedReader(ctx, transferJob.sourceUri, ConnectionService.mtu)
             val service = ConnectionService.leService ?: throw IllegalStateException()
-            transfer = FileTransfer(service, reader,this, transferJob.destPath)
+            transfer = FileTransfer(service, reader, this, transferJob.destPath)
             transfer?.start()
         } catch (e: Exception) {
             Log.e(TAG, "Failed to initialize transfer")
diff --git a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt
index 8a19627..d96d8b8 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt
@@ -22,7 +22,6 @@
 
 package de.ccc.events.badge.card10.filetransfer
 
-import android.bluetooth.BluetoothAdapter
 import android.content.Intent
 import android.os.Bundle
 import android.util.Log
@@ -33,17 +32,13 @@ import android.widget.Button
 import android.widget.EditText
 import android.widget.ProgressBar
 import android.widget.TextView
-import androidx.annotation.UiThread
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.Fragment
-import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX
+import androidx.navigation.fragment.findNavController
 import de.ccc.events.badge.card10.R
 import de.ccc.events.badge.card10.common.ConnectionException
 import de.ccc.events.badge.card10.common.ConnectionService
 import de.ccc.events.badge.card10.common.GattListener
-import de.ccc.events.badge.card10.main.MainFragment
-import java.lang.Exception
-import java.lang.IllegalStateException
 
 private const val TAG = "FileTransferFragment"
 private const val INTENT_RESULT_CODE_FILE = 1
@@ -166,17 +161,14 @@ class FileTransferFragment : Fragment(), GattListener, FileTransferListener{
 
     private fun showError(message: String?) {
         val ctx = context ?: throw IllegalStateException()
-        val fm = fragmentManager ?: throw IllegalStateException()
+        val controller = findNavController()
 
         val errorDialog =
             AlertDialog.Builder(ctx)
                 .setMessage(message ?: getString(R.string.connection_error_generic))
                 .setPositiveButton(R.string.dialog_action_ok) {
                     dialog, _ -> dialog.dismiss()
-                    fm.beginTransaction()
-                        .replace(R.id.fragment_container, MainFragment())
-                        .addToBackStack(null)
-                        .commit()
+                    controller.popBackStack()
                 }
                 .show()
     }
diff --git a/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt
index f741afa..52aaa78 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt
@@ -22,7 +22,6 @@
 
 package de.ccc.events.badge.card10.hatchery
 
-import android.net.Uri
 import android.os.AsyncTask
 import android.os.Bundle
 import android.util.Log
@@ -32,10 +31,10 @@ import android.view.ViewGroup
 import androidx.appcompat.app.AlertDialog
 import androidx.core.net.toUri
 import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
+import androidx.navigation.NavController
+import androidx.navigation.fragment.findNavController
 import de.ccc.events.badge.card10.R
 import de.ccc.events.badge.card10.common.LoadingDialog
-import de.ccc.events.badge.card10.filetransfer.BatchTransferFragment
 import de.ccc.events.badge.card10.filetransfer.TransferJob
 import kotlinx.android.synthetic.main.app_detail_fragment.*
 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
@@ -50,9 +49,8 @@ class AppDetailFragment : Fragment() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        app = AppDetailFragmentArgs.fromBundle(requireArguments()).app
 
-        val bundle = arguments ?: throw IllegalStateException()
-        app = bundle.getParcelable<App>("app") ?: throw IllegalStateException()
     }
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
@@ -69,7 +67,7 @@ class AppDetailFragment : Fragment() {
             val ctx = activity ?: throw java.lang.IllegalStateException()
 
             val loadingDialog = LoadingDialog()
-            loadingDialog.show(fragmentManager, "loading")
+            loadingDialog.show(requireFragmentManager(), "loading")
 
             val errorDialog =
                 AlertDialog.Builder(ctx).setMessage(R.string.hatchery_error_generic)
@@ -78,8 +76,8 @@ class AppDetailFragment : Fragment() {
                     ) { dialog, _ -> dialog.dismiss() }
                     .create()
 
-            val fm = fragmentManager ?: throw java.lang.IllegalStateException()
-            ReleaseDownload(app, ctx.cacheDir, loadingDialog, errorDialog, fm).execute()
+            val nav = findNavController();
+            ReleaseDownload(app, ctx.cacheDir, loadingDialog, errorDialog, nav).execute()
         }
     }
 
@@ -88,7 +86,7 @@ class AppDetailFragment : Fragment() {
         private val cacheDir: File,
         private val loadingDialog: LoadingDialog,
         private val errorDialog: AlertDialog,
-        private val fragmentManager: FragmentManager
+        private val navController: NavController
     ) : AsyncTask<Void, Void, List<TransferJob>?>() {
 
         override fun doInBackground(vararg p0: Void?): List<TransferJob>? {
@@ -137,16 +135,9 @@ class AppDetailFragment : Fragment() {
             }
 
             loadingDialog.dismiss()
+            val nav = AppDetailFragmentDirections.startBatchTransfer(jobs.toTypedArray())
+            navController.navigate(nav)
 
-            val bundle = Bundle()
-            bundle.putParcelableArray("jobs", jobs.toTypedArray())
-            val fragment = BatchTransferFragment()
-            fragment.arguments = bundle
-
-            fragmentManager.beginTransaction()
-                .replace(R.id.fragment_container, fragment)
-                .addToBackStack(null)
-                .commit()
         }
 
         fun createLauncher(slug: String, cacheDir: File): TransferJob {
diff --git a/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppListFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppListFragment.kt
index cbf922b..a09cec2 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppListFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppListFragment.kt
@@ -30,6 +30,7 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import de.ccc.events.badge.card10.R
@@ -56,21 +57,12 @@ class AppListFragment : Fragment() {
         recyclerView.adapter = listAdapter
 
         val loadingDialog = LoadingDialog()
-        loadingDialog.show(fragmentManager, "loading")
+        loadingDialog.show(requireFragmentManager(), "loading")
         DownloadTask(activity as Context, listAdapter, loadingDialog).execute()
     }
 
     private fun onAppClicked(app: App) {
-        val bundle = Bundle()
-        bundle.putParcelable("app", app)
-        val fragment = AppDetailFragment()
-        fragment.arguments = bundle
-
-        val fragmentManager = activity?.supportFragmentManager ?: throw IllegalStateException()
-        fragmentManager.beginTransaction()
-            .replace(R.id.fragment_container, fragment)
-            .addToBackStack(null)
-            .commit()
+        findNavController().navigate(AppListFragmentDirections.actionAppListFragmentToAppDetailFragment(app))
     }
 
     private class DownloadTask(
diff --git a/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt
index fda3416..93a46b3 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt
@@ -27,41 +27,38 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.widget.Button
-import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
 import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX
 import de.ccc.events.badge.card10.R
 import de.ccc.events.badge.card10.common.ConnectionService
 import de.ccc.events.badge.card10.common.GattListener
-import de.ccc.events.badge.card10.filetransfer.FileTransferFragment
-import de.ccc.events.badge.card10.hatchery.AppListFragment
-import de.ccc.events.badge.card10.mood.MoodFragment
-import de.ccc.events.badge.card10.scanner.ScannerFragment
-import de.ccc.events.badge.card10.sparkle.BeautifulFragment
 import de.ccc.events.badge.card10.time.TimeUpdateDialog
 import kotlinx.android.synthetic.main.main_fragment.*
-import java.lang.IllegalStateException
-import java.sql.Connection
 
 class MainFragment : Fragment(), GattListener {
     private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
-        inflater.inflate(R.layout.main_fragment, container, false)
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        if(activity!!.intent.action == "application/x.card10.app") {
+            findNavController().navigate(MainFragmentDirections.Installer())
+            return null
+        }
+        return inflater.inflate(R.layout.main_fragment, container, false)
+    }
+
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        button_pair.setOnClickListener { startFragment(ScannerFragment()) }
-        button_mood.setOnClickListener { startFragment(MoodFragment()) }
-        button_beautiful.setOnClickListener { startFragment(BeautifulFragment()) }
-        button_hatchery.setOnClickListener { startFragment(AppListFragment()) }
-        button_send.setOnClickListener { startFragment(FileTransferFragment()) }
+        val con = findNavController()
+        button_pair.setOnClickListener { con.navigate(MainFragmentDirections.Scan()) }
+        button_mood.setOnClickListener { con.navigate(MainFragmentDirections.Mood()) }
+        button_beautiful.setOnClickListener { con.navigate(MainFragmentDirections.Beautiful()) }
+        button_hatchery.setOnClickListener { con.navigate(MainFragmentDirections.AppList()) }
+        button_send.setOnClickListener { con.navigate(MainFragmentDirections.Transfer()) }
 
         button_set_time.setOnClickListener {
             val dialogFragment = TimeUpdateDialog()
-            dialogFragment.show(fragmentManager, "time")
+            dialogFragment.show(fragmentManager!!, "time")
             dialogFragment.setTime()
             dialogFragment.dismiss()
         }
@@ -81,12 +78,6 @@ class MainFragment : Fragment(), GattListener {
         }
     }
 
-    private fun startFragment(fragment: Fragment) {
-        fragmentManager!!.beginTransaction()
-            .replace(R.id.fragment_container, fragment)
-            .addToBackStack(null)
-            .commit()
-    }
 
     private fun showConnectedView() {
         activity?.runOnUiThread {
diff --git a/app/src/main/java/de/ccc/events/badge/card10/mood/MoodFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/mood/MoodFragment.kt
index f9224c5..130a0be 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/mood/MoodFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/mood/MoodFragment.kt
@@ -32,6 +32,7 @@ import de.ccc.events.badge.card10.common.ConnectionService
 import de.ccc.events.badge.card10.time.Card10Service
 import kotlinx.android.synthetic.main.mood_fragment.*
 
+@ExperimentalUnsignedTypes
 class MoodFragment : Fragment() {
     private var card10Service: Card10Service? = null
 
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 15057fc..38a614e 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:id="@+id/fragment_container"
-             android:layout_width="match_parent"
-             android:layout_height="match_parent"/>
+<fragment
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/nav_host_fragment"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        app:defaultNavHost="true"
+        app:navGraph="@navigation/nav_graph"/>
+
diff --git a/app/src/main/res/layout/disconnected_fragment.xml b/app/src/main/res/layout/disconnected_fragment.xml
new file mode 100644
index 0000000..1042893
--- /dev/null
+++ b/app/src/main/res/layout/disconnected_fragment.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                                                   android:layout_width="match_parent"
+                                                   android:layout_height="match_parent"
+                                                   xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/image_logo"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/card10_logo_text"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="parent"/>
+
+    <TextView android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/label_status"
+              android:layout_marginTop="@dimen/logo_margin_bottom"
+              android:gravity="center"
+              android:text="@string/main_label_not_connected"
+              app:layout_constraintLeft_toLeftOf="parent"
+              app:layout_constraintRight_toRightOf="parent"
+              app:layout_constraintTop_toBottomOf="@+id/image_logo"/>
+
+    <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent"
+                                                       android:layout_height="0dp"
+                                                       app:layout_constraintTop_toBottomOf="@+id/label_status"
+                                                       app:layout_constraintBottom_toBottomOf="parent"
+                                                       android:id="@+id/container_disconnected">
+        <Button android:id="@+id/button_pair"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/main_button_pair"
+                android:layout_marginTop="8dp"
+                style="@style/MainButton"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintLeft_toLeftOf="parent"
+                app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/>
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
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..d331c9f
--- /dev/null
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            android:id="@+id/nav_graph" app:startDestination="@id/mainFragment">
+
+    <fragment android:id="@+id/mainFragment" android:name="de.ccc.events.badge.card10.main.MainFragment"
+              android:label="MainFragment" tools:layout="@layout/main_fragment">
+
+        <action android:id="@+id/Mood" app:destination="@id/moodFragment"/>
+        <action android:id="@+id/Beautiful" app:destination="@id/beautifulFragment"/>
+        <action android:id="@+id/AppList" app:destination="@id/appListFragment"/>
+        <action android:id="@+id/Transfer"
+                app:destination="@id/fileTransferFragment"/>
+        <action android:id="@+id/Installer" app:destination="@id/installerFragment"/>
+        <action android:id="@+id/Scan" app:destination="@id/scannerFragment"/>
+    </fragment>
+    <fragment android:id="@+id/installerFragment" android:name="de.ccc.events.badge.card10.installer.InstallerFragment"
+              android:label="InstallerFragment" tools:layout="@layout/installer_fragment"/>
+    <fragment android:id="@+id/scannerFragment" android:name="de.ccc.events.badge.card10.scanner.ScannerFragment"
+              android:label="ScannerFragment" tools:layout="@layout/scanner_fragment"/>
+    <fragment android:id="@+id/moodFragment" android:name="de.ccc.events.badge.card10.mood.MoodFragment"
+              android:label="Mood" tools:layout="@layout/mood_fragment"/>
+    <fragment android:id="@+id/beautifulFragment" android:name="de.ccc.events.badge.card10.sparkle.BeautifulFragment"
+              android:label="BeautifulFragment" tools:layout="@layout/beautiful_fragment"/>
+    <fragment android:id="@+id/appListFragment" android:name="de.ccc.events.badge.card10.hatchery.AppListFragment"
+              android:label="AppListFragment" tools:layout="@layout/app_list_fragment">
+        <action android:id="@+id/action_appListFragment_to_appDetailFragment" app:destination="@id/appDetailFragment"/>
+    </fragment>
+    <fragment android:id="@+id/fileTransferFragment"
+              android:name="de.ccc.events.badge.card10.filetransfer.FileTransferFragment"
+              android:label="FileTransferFragment" tools:layout="@layout/file_transfer_fragment"/>
+    <fragment android:id="@+id/appDetailFragment" android:name="de.ccc.events.badge.card10.hatchery.AppDetailFragment"
+              android:label="AppDetailFragment" tools:layout="@layout/app_detail_fragment">
+        <action android:id="@+id/startBatchTransfer"
+                app:destination="@id/batchTransferFragment"/>
+        <argument android:name="app" app:argType="de.ccc.events.badge.card10.hatchery.App"/>
+    </fragment>
+    <fragment android:id="@+id/batchTransferFragment"
+              android:name="de.ccc.events.badge.card10.filetransfer.BatchTransferFragment"
+              android:label="BatchTransferFragment">
+        <argument android:name="jobs" app:argType="de.ccc.events.badge.card10.filetransfer.TransferJob[]"/>
+    </fragment>
+</navigation>
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index e132b9b..7e99394 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,6 +2,8 @@
 
 buildscript {
     ext.kotlin_version = '1.3.40'
+    ext.nav_version = "2.2.0-alpha01"
+
     repositories {
         google()
         jcenter()
@@ -10,6 +12,9 @@ buildscript {
     dependencies {
         classpath 'com.android.tools.build:gradle:3.4.2'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
+
+
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
     }
-- 
GitLab