diff --git a/mailbox-android/build.gradle b/mailbox-android/build.gradle
index f840547001745a91938a01b955924249d5be6bbf..744978cd48c4a17c8b4ca50a2abe1ac1d443f6c7 100644
--- a/mailbox-android/build.gradle
+++ b/mailbox-android/build.gradle
@@ -30,6 +30,9 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
         }
     }
+    buildFeatures {
+        viewBinding true
+    }
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
@@ -66,6 +69,10 @@ dependencies {
     implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
     implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
 
+    def nav_version = "2.4.0"
+    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
+    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
+
     implementation "androidx.constraintlayout:constraintlayout:$androidx_constraintlayout_version"
     implementation "com.google.android.material:material:$google_material_version"
     implementation "com.google.dagger:hilt-android:$hilt_version"
diff --git a/mailbox-android/src/main/AndroidManifest.xml b/mailbox-android/src/main/AndroidManifest.xml
index ee9849f4d42f338fdbba1853db241dc546aeca6b..4466f33c0ae170dc3a788afb1c808dce5cbeb4ac 100644
--- a/mailbox-android/src/main/AndroidManifest.xml
+++ b/mailbox-android/src/main/AndroidManifest.xml
@@ -20,7 +20,7 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/Theme.Briarmailbox">
+        android:theme="@style/Theme.BriarMailbox">
 
         <service android:name=".android.MailboxService" />
 
@@ -33,6 +33,7 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".android.ui.OnboardingActivity" />
 
         <receiver android:name=".core.system.AlarmReceiver" />
 
diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt
index 7cdf4dfa16a8b100f4af24653a055ceaae941278..48661e9defc72c33c03b49f600c9d892530d7d0b 100644
--- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt
+++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxViewModel.kt
@@ -30,6 +30,7 @@ import kotlinx.coroutines.flow.StateFlow
 import org.briarproject.android.dontkillmelib.DozeHelper
 import org.briarproject.mailbox.core.lifecycle.LifecycleManager
 import org.briarproject.mailbox.core.lifecycle.LifecycleManager.LifecycleState
+import org.briarproject.mailbox.core.setup.SetupManager
 import org.briarproject.mailbox.core.system.DozeWatchdog
 import javax.inject.Inject
 import kotlin.concurrent.thread
@@ -41,6 +42,7 @@ class MailboxViewModel @Inject constructor(
     private val dozeWatchdog: DozeWatchdog,
     handle: SavedStateHandle,
     private val lifecycleManager: LifecycleManager,
+    private val setupManager: SetupManager,
 ) : AndroidViewModel(app) {
 
     val needToShowDoNotKillMeFragment get() = dozeHelper.needToShowDoNotKillMeFragment(app)
@@ -53,6 +55,8 @@ class MailboxViewModel @Inject constructor(
 
     val lifecycleState: StateFlow<LifecycleState> = lifecycleManager.lifecycleStateFlow
 
+    val isSetUp: Boolean get() = setupManager.hasDb
+
     @UiThread
     fun onDoNotKillComplete() {
         _doNotKillComplete.value = true
diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt
index 11932673a90c7c8fa643eaf4c379f5ebb0ccdd15..1aad5c7f10dd0a913033919c3ab4eef732e32d22 100644
--- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt
+++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MainActivity.kt
@@ -19,35 +19,45 @@
 
 package org.briarproject.mailbox.android
 
+import android.content.Intent
 import android.os.Bundle
 import androidx.activity.viewModels
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
+import androidx.navigation.NavController
+import androidx.navigation.fragment.NavHostFragment
 import dagger.hilt.android.AndroidEntryPoint
 import org.briarproject.android.dontkillmelib.PowerUtils.needsDozeWhitelisting
 import org.briarproject.mailbox.R
+import org.briarproject.mailbox.android.ui.OnboardingActivity
 
 @AndroidEntryPoint
 class MainActivity : AppCompatActivity() {
 
     private val viewModel: MailboxViewModel by viewModels()
+    private val nav: NavController by lazy {
+        val navHostFragment =
+            supportFragmentManager.findFragmentById(R.id.fragmentContainer) as NavHostFragment
+        navHostFragment.navController
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
 
         viewModel.doNotKillComplete.observe(this) { complete ->
-            if (complete) showFragment(MainFragment())
+            if (complete) nav.popBackStack()
         }
 
         if (savedInstanceState == null) {
-            val f = if (viewModel.needToShowDoNotKillMeFragment) {
-                DoNotKillMeFragment()
-            } else {
-                MainFragment()
+            if (viewModel.needToShowDoNotKillMeFragment) {
+                nav.navigate(R.id.action_mainFragment_to_doNotKillMeFragment)
+            }
+            if (!viewModel.isSetUp) {
+                Intent(this, OnboardingActivity::class.java).also { i ->
+                    startActivity(i)
+                }
             }
-            showFragment(f)
         }
     }
 
@@ -58,16 +68,10 @@ class MainActivity : AppCompatActivity() {
         }
     }
 
-    private fun showFragment(f: Fragment) {
-        supportFragmentManager.beginTransaction()
-            .replace(R.id.fragmentContainer, f)
-            .commitNow()
-    }
-
     private fun showDozeDialog() = AlertDialog.Builder(this)
         .setMessage(R.string.warning_dozed)
         .setPositiveButton(R.string.fix) { dialog, _ ->
-            showFragment(DoNotKillMeFragment())
+            nav.navigate(R.id.action_mainFragment_to_doNotKillMeFragment)
             dialog.dismiss()
         }
         .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..acfbeaf735a150182887a3fb0503221e896e319c
--- /dev/null
+++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingActivity.kt
@@ -0,0 +1,32 @@
+/*
+ *     Briar Mailbox
+ *     Copyright (C) 2021-2022  The Briar Project
+ *
+ *     This program is free software: you can redistribute it and/or modify
+ *     it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ *     You should have received a copy of the GNU Affero General Public License
+ *     along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.briarproject.mailbox.android.ui
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import org.briarproject.mailbox.R
+
+class OnboardingActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_onboarding)
+    }
+}
diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c332dede856610782c5d6701bbf812cd0c3815e6
--- /dev/null
+++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt
@@ -0,0 +1,143 @@
+/*
+ *     Briar Mailbox
+ *     Copyright (C) 2021-2022  The Briar Project
+ *
+ *     This program is free software: you can redistribute it and/or modify
+ *     it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ *     You should have received a copy of the GNU Affero General Public License
+ *     along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.briarproject.mailbox.android.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.core.content.ContextCompat.getColorStateList
+import androidx.core.widget.ImageViewCompat
+import androidx.fragment.app.Fragment
+import androidx.navigation.NavController
+import androidx.navigation.fragment.findNavController
+import org.briarproject.mailbox.R
+import org.briarproject.mailbox.databinding.FragmentOnboardingBinding
+
+class Onboarding1Fragment : OnboardingFragment(
+    number = 1,
+    icon = R.mipmap.ic_launcher_round,
+    title = R.string.onboarding_1_title,
+    description = R.string.onboarding_1_description,
+    topButtonAction = { nav ->
+        nav.navigate(R.id.action_onboarding_1_to_2)
+    },
+    bottomButtonText = R.string.button_skip_intro,
+    bottomButtonAction = {
+        requireActivity().supportFinishAfterTransition()
+    },
+)
+
+class Onboarding2Fragment : OnboardingFragment(
+    number = 2,
+    icon = R.mipmap.ic_launcher_round,
+    title = R.string.onboarding_2_title,
+    description = R.string.onboarding_2_description,
+    topButtonAction = { nav ->
+        nav.navigate(R.id.action_onboarding_2_to_3)
+    },
+    bottomButtonAction = { nav ->
+        nav.popBackStack()
+    },
+)
+
+class Onboarding3Fragment : OnboardingFragment(
+    number = 3,
+    icon = R.mipmap.ic_launcher_round,
+    title = R.string.onboarding_3_title,
+    description = R.string.onboarding_3_description,
+    topButtonAction = { nav ->
+        nav.navigate(R.id.action_onboarding_3_to_4)
+    },
+    bottomButtonAction = { nav ->
+        nav.popBackStack()
+    },
+)
+
+class Onboarding4Fragment : OnboardingFragment(
+    number = 4,
+    icon = R.mipmap.ic_launcher_round,
+    title = R.string.onboarding_4_title,
+    description = R.string.onboarding_4_description,
+    topButtonAction = {
+        requireActivity().supportFinishAfterTransition()
+    },
+    bottomButtonAction = { nav ->
+        nav.popBackStack()
+    },
+)
+
+abstract class OnboardingFragment(
+    private val number: Int,
+    @DrawableRes
+    private val icon: Int,
+    @StringRes
+    private val title: Int,
+    @StringRes
+    private val description: Int,
+    @StringRes
+    private val bottomButtonText: Int = R.string.button_back,
+    private val topButtonAction: Fragment.(NavController) -> Unit,
+    private val bottomButtonAction: Fragment.(NavController) -> Unit,
+) : Fragment() {
+
+    private var _ui: FragmentOnboardingBinding? = null
+
+    /**
+     * This property is only valid between [onCreateView] and [onDestroyView].
+     */
+    private val ui get() = _ui!!
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?,
+    ): View? {
+        _ui = FragmentOnboardingBinding.inflate(inflater, container, false)
+        return ui.root
+    }
+
+    override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
+        ui.icon.setImageResource(icon)
+        ui.title.setText(title)
+        ui.description.setText(description)
+        listOf(ui.bullet1, ui.bullet2, ui.bullet3, ui.bullet4).forEachIndexed { i, imageView ->
+            val color = if (i + 1 <= number) R.color.briar_green else R.color.briar_night
+            val tintList = getColorStateList(requireContext(), color)
+            ImageViewCompat.setImageTintList(imageView, tintList)
+        }
+        val nav = findNavController()
+        ui.topButton.setOnClickListener {
+            topButtonAction(nav)
+        }
+        ui.bottomButton.setText(bottomButtonText)
+        ui.bottomButton.setOnClickListener {
+            bottomButtonAction(nav)
+        }
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _ui = null
+    }
+
+}
diff --git a/mailbox-android/src/main/res/anim/enter.xml b/mailbox-android/src/main/res/anim/enter.xml
new file mode 100644
index 0000000000000000000000000000000000000000..32b84f2dac6e4ebe6ccca6ace3a222d26d8ac7c4
--- /dev/null
+++ b/mailbox-android/src/main/res/anim/enter.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+	<!-- slide in from right -->
+	<translate
+		android:duration="@integer/animationSpeed"
+		android:fromXDelta="100%p"
+		android:interpolator="@android:interpolator/decelerate_quad"
+		android:toXDelta="0" />
+
+</set>
diff --git a/mailbox-android/src/main/res/anim/exit.xml b/mailbox-android/src/main/res/anim/exit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..205435cca6e4605e75c01af6f329e5174e9466f1
--- /dev/null
+++ b/mailbox-android/src/main/res/anim/exit.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+	android:duration="@integer/animationSpeed"
+	android:fromAlpha="1.0"
+	android:interpolator="@android:interpolator/accelerate_quad"
+	android:toAlpha="0.0" />
diff --git a/mailbox-android/src/main/res/anim/pop_enter.xml b/mailbox-android/src/main/res/anim/pop_enter.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4b3a8d6092c7ab619e25b0b1db87ffba0c78b183
--- /dev/null
+++ b/mailbox-android/src/main/res/anim/pop_enter.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@integer/animationSpeed"
+    android:fromAlpha="0.0"
+    android:interpolator="@android:interpolator/decelerate_quad"
+    android:toAlpha="1.0" />
diff --git a/mailbox-android/src/main/res/anim/pop_exit.xml b/mailbox-android/src/main/res/anim/pop_exit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2c514d824c58fad5c2a2738232c250403b56f238
--- /dev/null
+++ b/mailbox-android/src/main/res/anim/pop_exit.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- slide out to right -->
+    <translate
+        android:duration="@integer/animationSpeed"
+        android:fromXDelta="0"
+        android:interpolator="@android:interpolator/accelerate_quad"
+        android:toXDelta="100%p" />
+
+</set>
diff --git a/mailbox-android/src/main/res/drawable/ic_circle.xml b/mailbox-android/src/main/res/drawable/ic_circle.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bf54d3e4994316b105c938b66738ec29df605a59
--- /dev/null
+++ b/mailbox-android/src/main/res/drawable/ic_circle.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?attr/colorControlNormal"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2z" />
+</vector>
diff --git a/mailbox-android/src/main/res/layout-land/fragment_onboarding.xml b/mailbox-android/src/main/res/layout-land/fragment_onboarding.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e5492eece8ea25e46c29f05e6382a5d1dd8ea7d8
--- /dev/null
+++ b/mailbox-android/src/main/res/layout-land/fragment_onboarding.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="32dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintEnd_toStartOf="@+id/guideline"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:srcCompat="@mipmap/ic_launcher"
+        tools:ignore="ContentDescription" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_percent="0.5" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:gravity="center"
+        android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium"
+        app:layout_constraintBottom_toTopOf="@+id/description"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/guideline"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_chainStyle="packed"
+        tools:text="@string/onboarding_1_title" />
+
+    <TextView
+        android:id="@+id/description"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+        app:layout_constraintBottom_toTopOf="@+id/bullet1"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/guideline"
+        app:layout_constraintTop_toBottomOf="@+id/title"
+        tools:text="@string/onboarding_1_description" />
+
+    <ImageView
+        android:id="@+id/bullet1"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toStartOf="@id/bullet2"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintStart_toStartOf="@+id/guideline"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorSecondary" />
+
+    <ImageView
+        android:id="@+id/bullet2"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginStart="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toStartOf="@id/bullet3"
+        app:layout_constraintStart_toEndOf="@+id/bullet1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorPrimary" />
+
+    <ImageView
+        android:id="@+id/bullet3"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginStart="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toStartOf="@id/bullet4"
+        app:layout_constraintStart_toEndOf="@+id/bullet2"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorPrimary" />
+
+    <ImageView
+        android:id="@+id/bullet4"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginStart="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="16dp"
+        android:layout_marginRight="16dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/bullet3"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorPrimary" />
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/topButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginEnd="16dp"
+        android:layout_marginRight="16dp"
+        android:text="@string/button_continue"
+        app:layout_constraintBottom_toTopOf="@+id/bottomButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/guideline"
+        app:layout_constraintTop_toBottomOf="@+id/bullet1"
+        app:layout_constraintVertical_bias="1.0" />
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/bottomButton"
+        style="@style/TextButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginEnd="16dp"
+        android:layout_marginRight="16dp"
+        android:text="@string/button_skip_intro"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/guideline"
+        app:layout_constraintTop_toBottomOf="@+id/topButton" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/mailbox-android/src/main/res/layout/activity_main.xml b/mailbox-android/src/main/res/layout/activity_main.xml
index 0e985efea7541f7fc4564c0403128f4b0d668dbe..d8d6ae61cc5fef38779d2d922b3e48d915bd9833 100644
--- a/mailbox-android/src/main/res/layout/activity_main.xml
+++ b/mailbox-android/src/main/res/layout/activity_main.xml
@@ -1,7 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.fragment.app.FragmentContainerView 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/fragmentContainer"
+    android:name="androidx.navigation.fragment.NavHostFragment"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    app:defaultNavHost="true"
+    app:navGraph="@navigation/nav_main"
     tools:context=".android.MainActivity" />
diff --git a/mailbox-android/src/main/res/layout/activity_onboarding.xml b/mailbox-android/src/main/res/layout/activity_onboarding.xml
new file mode 100644
index 0000000000000000000000000000000000000000..24bf07ef9e8889559358519445fa8267bcad5b79
--- /dev/null
+++ b/mailbox-android/src/main/res/layout/activity_onboarding.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.fragment.app.FragmentContainerView 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/fragmentContainer"
+    android:name="androidx.navigation.fragment.NavHostFragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:defaultNavHost="true"
+    app:navGraph="@navigation/nav_onboarding"
+    tools:context=".android.ui.OnboardingActivity" />
diff --git a/mailbox-android/src/main/res/layout/fragment_onboarding.xml b/mailbox-android/src/main/res/layout/fragment_onboarding.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2c07cce94fef67788a0a39c165678d589caa9038
--- /dev/null
+++ b/mailbox-android/src/main/res/layout/fragment_onboarding.xml
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="32dp"
+        app:layout_constraintBottom_toTopOf="@+id/title"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:srcCompat="@mipmap/ic_launcher"
+        tools:ignore="ContentDescription" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:gravity="center"
+        android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium"
+        app:layout_constraintBottom_toTopOf="@+id/description"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/icon"
+        app:layout_constraintVertical_chainStyle="packed"
+        tools:text="@string/onboarding_1_title" />
+
+    <TextView
+        android:id="@+id/description"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+        app:layout_constraintBottom_toTopOf="@+id/bullet1"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/title"
+        tools:text="@string/onboarding_1_description" />
+
+    <ImageView
+        android:id="@+id/bullet1"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toStartOf="@id/bullet2"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorSecondary" />
+
+    <ImageView
+        android:id="@+id/bullet2"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginStart="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toStartOf="@id/bullet3"
+        app:layout_constraintStart_toEndOf="@+id/bullet1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorPrimary" />
+
+    <ImageView
+        android:id="@+id/bullet3"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginStart="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toStartOf="@id/bullet4"
+        app:layout_constraintStart_toEndOf="@+id/bullet2"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorPrimary" />
+
+    <ImageView
+        android:id="@+id/bullet4"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:layout_marginStart="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="16dp"
+        android:layout_marginRight="16dp"
+        android:layout_marginBottom="16dp"
+        app:layout_constraintBottom_toTopOf="@+id/topButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/bullet3"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:srcCompat="@drawable/ic_circle"
+        app:tint="?attr/colorPrimary" />
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/topButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:text="@string/button_continue"
+        app:layout_constraintBottom_toTopOf="@+id/bottomButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/bullet1"
+        app:layout_constraintVertical_bias="1.0" />
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/bottomButton"
+        style="@style/TextButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/button_skip_intro"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/topButton" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/mailbox-android/src/main/res/navigation/nav_main.xml b/mailbox-android/src/main/res/navigation/nav_main.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2a91515276cb7f5c93beb2eefed9db87642dba9e
--- /dev/null
+++ b/mailbox-android/src/main/res/navigation/nav_main.xml
@@ -0,0 +1,19 @@
+<?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"
+    android:id="@+id/nav_onboarding"
+    app:startDestination="@id/mainFragment">
+
+    <fragment
+        android:id="@+id/mainFragment"
+        android:name="org.briarproject.mailbox.android.MainFragment"
+        android:label="MainFragment">
+        <action
+            android:id="@+id/action_mainFragment_to_doNotKillMeFragment"
+            app:destination="@id/doNotKillMeFragment" />
+    </fragment>
+    <fragment
+        android:id="@+id/doNotKillMeFragment"
+        android:name="org.briarproject.mailbox.android.DoNotKillMeFragment"
+        android:label="DoNotKillMeFragment" />
+</navigation>
diff --git a/mailbox-android/src/main/res/navigation/nav_onboarding.xml b/mailbox-android/src/main/res/navigation/nav_onboarding.xml
new file mode 100644
index 0000000000000000000000000000000000000000..74a5c75501577c7cd9aba3fb8bdb7f9f9b350cd6
--- /dev/null
+++ b/mailbox-android/src/main/res/navigation/nav_onboarding.xml
@@ -0,0 +1,51 @@
+<?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_onboarding"
+    app:startDestination="@id/onboarding1Fragment">
+    <fragment
+        android:id="@+id/onboarding1Fragment"
+        android:name="org.briarproject.mailbox.android.ui.Onboarding1Fragment"
+        android:label="Onboarding1Fragment"
+        tools:layout="@layout/fragment_onboarding">
+        <action
+            android:id="@+id/action_onboarding_1_to_2"
+            app:destination="@id/onboarding2Fragment"
+            app:enterAnim="@anim/enter"
+            app:exitAnim="@anim/exit"
+            app:popEnterAnim="@anim/pop_enter"
+            app:popExitAnim="@anim/pop_exit" />
+    </fragment>
+    <fragment
+        android:id="@+id/onboarding2Fragment"
+        android:name="org.briarproject.mailbox.android.ui.Onboarding2Fragment"
+        android:label="Onboarding2Fragment"
+        tools:layout="@layout/fragment_onboarding">
+        <action
+            android:id="@+id/action_onboarding_2_to_3"
+            app:destination="@id/onboarding3Fragment"
+            app:enterAnim="@anim/enter"
+            app:exitAnim="@anim/exit"
+            app:popEnterAnim="@anim/pop_enter"
+            app:popExitAnim="@anim/pop_exit" />
+    </fragment>
+    <fragment
+        android:id="@+id/onboarding3Fragment"
+        android:name="org.briarproject.mailbox.android.ui.Onboarding3Fragment"
+        android:label="Onboarding3Fragment"
+        tools:layout="@layout/fragment_onboarding">
+        <action
+            android:id="@+id/action_onboarding_3_to_4"
+            app:destination="@id/onboarding4Fragment"
+            app:enterAnim="@anim/enter"
+            app:exitAnim="@anim/exit"
+            app:popEnterAnim="@anim/pop_enter"
+            app:popExitAnim="@anim/pop_exit" />
+    </fragment>
+    <fragment
+        android:id="@+id/onboarding4Fragment"
+        android:name="org.briarproject.mailbox.android.ui.Onboarding4Fragment"
+        android:label="Onboarding4Fragment"
+        tools:layout="@layout/fragment_onboarding" />
+</navigation>
diff --git a/mailbox-android/src/main/res/values-night/themes.xml b/mailbox-android/src/main/res/values-night/themes.xml
index 8b6f2d272f57a825e5e3f2bbf67c1443a2df7b40..0a74b49e797e68c61913bdbce8a5f3490e94e823 100644
--- a/mailbox-android/src/main/res/values-night/themes.xml
+++ b/mailbox-android/src/main/res/values-night/themes.xml
@@ -1,10 +1,13 @@
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
     <!-- Base application theme. -->
-    <style name="Theme.Briarmailbox" parent="Theme.AppCompat.Light.DarkActionBar">
+    <style name="Theme.BriarMailbox" parent="Theme.Material3.DayNight.NoActionBar">
         <!-- Primary brand color. -->
-        <item name="colorPrimary">@color/purple_200</item>
-        <item name="colorPrimaryDark">@color/purple_700</item>
-        <item name="colorAccent">@color/teal_200</item>
+        <item name="colorPrimary">@color/briar_night</item>
+        <item name="colorPrimaryDark">#1F78D1</item>
+        <item name="colorSecondary">@color/briar_green</item>
         <!-- Customize your theme here. -->
+        <item name="android:background">@color/black</item>
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/background</item>
+        <item name="colorOnPrimary">@color/white</item>
     </style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/mailbox-android/src/main/res/values/attrs.xml b/mailbox-android/src/main/res/values/attrs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3704cec434d97f94f6b416d1c20c0b9f4d4598bf
--- /dev/null
+++ b/mailbox-android/src/main/res/values/attrs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <integer name="animationSpeed">@android:integer/config_mediumAnimTime</integer>
+
+</resources>
diff --git a/mailbox-android/src/main/res/values/colors.xml b/mailbox-android/src/main/res/values/colors.xml
index f8c6127d327620c93d2b2d00342a68e97b98a48d..ff2144f45d60860c11f8a8f205593a3ca2095db4 100644
--- a/mailbox-android/src/main/res/values/colors.xml
+++ b/mailbox-android/src/main/res/values/colors.xml
@@ -7,4 +7,7 @@
     <color name="teal_700">#FF018786</color>
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>
+
+    <color name="briar_green">#74B816</color>
+    <color name="briar_night">#435B77</color>
 </resources>
\ No newline at end of file
diff --git a/mailbox-android/src/main/res/values/strings.xml b/mailbox-android/src/main/res/values/strings.xml
index 3622cbb1e26cd8a1e3057fbae1e02afedf0c257c..f895cbf16d6c4ef50fb3bcf8e54efd3c19e3041e 100644
--- a/mailbox-android/src/main/res/values/strings.xml
+++ b/mailbox-android/src/main/res/values/strings.xml
@@ -6,6 +6,18 @@
     <string name="start">Start mailbox</string>
     <string name="stop">Stop mailbox</string>
     <string name="wipe">Wipe mailbox</string>
+    <string name="button_continue">Continue</string>
+    <string name="button_skip_intro">Skip Intro</string>
+    <string name="button_back">Back</string>
+
+    <string name="onboarding_1_title">Stay Reachable</string>
+    <string name="onboarding_1_description">The Mailbox helps you to stay in touch with your contacts and synchronizes your messages to Briar.</string>
+    <string name="onboarding_2_title">Use a spare device</string>
+    <string name="onboarding_2_description">Install the mailbox app on a spare phone or tablet and leave it connected to power and WiFi. </string>
+    <string name="onboarding_3_title">Message delivery</string>
+    <string name="onboarding_3_description">When your Mailbox is online, your contacts can leave messages for you even if your Briar is offline. The Mailbox will receive the messages, and forward them to Briar when you come online.</string>
+    <string name="onboarding_4_title">Encrypted</string>
+    <string name="onboarding_4_description">Messages going through the Mailbox are encrypted and cannot be read by the Mailbox or this phone. The Mailbox simply forwards them to your Briar Messenger phone.</string>
 
     <!-- TODO: We might want to copy string from don't kill me lib,
           so translation memory can auto-translate most of them. -->
diff --git a/mailbox-android/src/main/res/values/styles.xml b/mailbox-android/src/main/res/values/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e547d159db5ad01f164b7431e754dadc57e4ab0b
--- /dev/null
+++ b/mailbox-android/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="TextButton" parent="Widget.Material3.Button.TextButton">
+        <item name="android:textColor">?attr/colorSecondary</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/mailbox-android/src/main/res/values/themes.xml b/mailbox-android/src/main/res/values/themes.xml
index 9d0dfedbc6008d4403b648f4fb6c3393cba66d18..ce3da2635558f317d3cddd6f5d339a7f751198c2 100644
--- a/mailbox-android/src/main/res/values/themes.xml
+++ b/mailbox-android/src/main/res/values/themes.xml
@@ -1,10 +1,11 @@
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools">
     <!-- Base application theme. -->
-    <style name="Theme.Briarmailbox" parent="Theme.AppCompat.Light.DarkActionBar">
+    <style name="Theme.BriarMailbox" parent="Theme.Material3.DayNight.NoActionBar">
         <!-- Primary brand color. -->
-        <item name="colorPrimary">@color/purple_500</item>
-        <item name="colorPrimaryDark">@color/purple_700</item>
-        <item name="colorAccent">@color/teal_200</item>
+        <item name="colorPrimary">@color/briar_night</item>
+        <item name="colorPrimaryDark">#1F78D1</item>
+        <item name="colorSecondary">@color/briar_green</item>
         <!-- Customize your theme here. -->
+        <item name="android:statusBarColor" tools:targetApi="l">@color/black</item>
     </style>
-</resources>
\ No newline at end of file
+</resources>