diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
index 51c75e79..24d0d95a 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
@@ -56,13 +56,16 @@ import org.moire.ultrasonic.model.ServerSettingsModel
 import org.moire.ultrasonic.provider.SearchSuggestionProvider
 import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport
 import org.moire.ultrasonic.service.MediaPlayerManager
+import org.moire.ultrasonic.service.MusicServiceFactory
 import org.moire.ultrasonic.service.RxBus
 import org.moire.ultrasonic.service.plusAssign
+import org.moire.ultrasonic.subsonic.DownloadHandler
 import org.moire.ultrasonic.util.Constants
 import org.moire.ultrasonic.util.InfoDialog
 import org.moire.ultrasonic.util.LocaleHelper
 import org.moire.ultrasonic.util.ServerColor
 import org.moire.ultrasonic.util.Settings
+import org.moire.ultrasonic.util.ShortcutUtil
 import org.moire.ultrasonic.util.Storage
 import org.moire.ultrasonic.util.UncaughtExceptionHandler
 import org.moire.ultrasonic.util.Util
@@ -212,6 +215,12 @@ class NavigationActivity : AppCompatActivity() {
             cachedServerCount = count ?: 0
             updateNavigationHeaderForServer()
         }
+
+        // Setup app shortcuts on supported devices, but not on first start, when the server
+        // is not configured yet.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && !UApp.instance!!.isFirstRun) {
+            ShortcutUtil.registerShortcuts(this)
+        }
     }
 
     private fun setupDrawerLayout(drawerLayout: DrawerLayout) {
@@ -389,6 +398,23 @@ class NavigationActivity : AppCompatActivity() {
         super.onNewIntent(intent)
         if (intent == null) return
 
+        if (intent.action == Constants.INTENT_PLAY_RANDOM_SONGS) {
+            val currentFragment = host?.childFragmentManager?.fragments?.last() ?: return
+            val service = MusicServiceFactory.getMusicService()
+            val musicDirectory = service.getRandomSongs(Settings.maxSongs)
+            val downloadHandler: DownloadHandler by inject()
+            downloadHandler.addTracksToMediaController(
+                songs = musicDirectory.getTracks(),
+                append = false,
+                playNext = false,
+                autoPlay = true,
+                shuffle = false,
+                fragment = currentFragment,
+                playlistName = null
+            )
+            return
+        }
+
         if (intent.getBooleanExtra(Constants.INTENT_SHOW_PLAYER, false)) {
             findNavController(R.id.nav_host_fragment).navigate(R.id.playerFragment)
             return
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt
index 351de91c..1db5cffe 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt
@@ -25,6 +25,7 @@ object Constants {
     const val CMD_PREVIOUS = "org.moire.ultrasonic.CMD_PREVIOUS"
     const val CMD_NEXT = "org.moire.ultrasonic.CMD_NEXT"
     const val INTENT_SHOW_PLAYER = "org.moire.ultrasonic.SHOW_PLAYER"
+    const val INTENT_PLAY_RANDOM_SONGS = "org.moire.ultrasonic.CMD_RANDOM_SONGS"
 
     // Legacy Preferences keys
     // Warning: Don't add any new here!
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/ShortcutUtil.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/ShortcutUtil.kt
new file mode 100644
index 00000000..7034d75b
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/ShortcutUtil.kt
@@ -0,0 +1,37 @@
+/*
+ * ShortcutUtil.kt
+ * Copyright (C) 2009-2023 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
+package org.moire.ultrasonic.util
+
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.ShortcutInfo
+import android.content.pm.ShortcutManager
+import android.graphics.drawable.Icon
+import android.os.Build
+import androidx.annotation.RequiresApi
+import org.moire.ultrasonic.R
+import org.moire.ultrasonic.activity.NavigationActivity
+
+object ShortcutUtil {
+    @RequiresApi(Build.VERSION_CODES.N_MR1)
+    fun registerShortcuts(activity: Activity) {
+        val shortcutIntent = Intent(activity, NavigationActivity::class.java).apply {
+            action = Constants.INTENT_PLAY_RANDOM_SONGS
+        }
+
+        val shortcut = ShortcutInfo.Builder(activity, "shortcut_play_random_songs")
+            .setShortLabel(activity.getString(R.string.shortcut_play_random_songs_short))
+            .setLongLabel(activity.getString(R.string.shortcut_play_random_songs_long))
+            .setIcon(Icon.createWithResource(activity, R.drawable.media_shuffle))
+            .setIntent(shortcutIntent)
+            .build()
+
+        val shortcutManager = activity.getSystemService(ShortcutManager::class.java)
+        shortcutManager?.dynamicShortcuts = listOf(shortcut)
+    }
+}
diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml
index 4f888cbb..73763394 100644
--- a/ultrasonic/src/main/res/values/strings.xml
+++ b/ultrasonic/src/main/res/values/strings.xml
@@ -456,6 +456,8 @@
     <string name="foreground_exception_text">Press on the play button on the media notification if it
     is still present, otherwise please open the app to start the playback and re-connect the session
     to the controller</string>
+    <string name="shortcut_play_random_songs_short">Random songs</string>
+    <string name="shortcut_play_random_songs_long">Play random songs</string>
 
 
 </resources>