diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt
index 6ff86d51b901fa375f3df06295905e8b4f9ef707..b755b7bba5b75211cb9e9e2f67f441a0300fa1fb 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt
@@ -211,6 +211,12 @@ class ChatlineFragment : ServiceBoundFragment() {
     }
 
     editorHelper.setOnEnterListener(::send)
+    editorHelper.setOnDownListener {
+      chatline.setText(modelHelper.chat.recentMessagesIndexDown(chatline.safeText))
+    }
+    editorHelper.setOnUpListener {
+      chatline.setText(modelHelper.chat.recentMessagesIndexUp())
+    }
     send.setOnClickListener { send() }
     send.setTooltip()
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/EditorHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/EditorHelper.kt
index cf3880b322be53a7d8465da3967238b3a78520f9..9b9fca38683ec50a263083772dffe72b61f4492a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/EditorHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/EditorHelper.kt
@@ -23,6 +23,7 @@ import android.text.Editable
 import android.text.TextWatcher
 import android.view.KeyEvent
 import android.view.inputmethod.EditorInfo
+import android.widget.Toast
 import androidx.annotation.ColorInt
 import androidx.annotation.StringRes
 import androidx.fragment.app.FragmentActivity
@@ -44,6 +45,8 @@ class EditorHelper(
   appearanceSettings: AppearanceSettings
 ) {
   private var enterListener: (() -> Unit)? = null
+  private var upListener: (() -> Unit)? = null
+  private var downListener: (() -> Unit)? = null
 
   private val mircColors = listOf(
     R.color.mircColor00, R.color.mircColor01, R.color.mircColor02, R.color.mircColor03,
@@ -124,8 +127,28 @@ class EditorHelper(
       )
     }
     editText.addTextChangedListener(textWatcher)
-    editText.setOnKeyListener { _, keyCode, event: KeyEvent? ->
-      if (event?.action == KeyEvent.ACTION_DOWN) {
+    editText.setOnKeyListener { _, keyCode, event: KeyEvent ->
+      val action = when (event.action) {
+        KeyEvent.ACTION_UP       -> "up"
+        KeyEvent.ACTION_DOWN     -> "down"
+        KeyEvent.ACTION_MULTIPLE -> "multiple"
+        else                     -> "unknown"
+      }
+      val key = when (keyCode) {
+        KeyEvent.KEYCODE_ENTER        -> "enter"
+        KeyEvent.KEYCODE_NUMPAD_ENTER -> "numpad_enter"
+        KeyEvent.KEYCODE_DPAD_DOWN    -> "down"
+        KeyEvent.KEYCODE_DPAD_UP      -> "up"
+        else                          -> "#$keyCode"
+      }
+      val modifiers = listOfNotNull(
+        if (event.isCtrlPressed) "ctrl" else null,
+        if (event.isAltPressed) "alt" else null,
+        if (event.isShiftPressed) "shift" else null
+      ).joinToString(", ")
+
+      Toast.makeText(editText.context, "$key $action $modifiers", Toast.LENGTH_SHORT).show()
+      if (event.action == KeyEvent.ACTION_DOWN) {
         if (event.isCtrlPressed && !event.isAltPressed) when (keyCode) {
           KeyEvent.KEYCODE_B -> {
             editText.toggleBold()
@@ -148,6 +171,14 @@ class EditorHelper(
             enterListener?.invoke()
             true
           }
+          KeyEvent.KEYCODE_DPAD_DOWN    -> {
+            downListener?.invoke()
+            true
+          }
+          KeyEvent.KEYCODE_DPAD_UP      -> {
+            upListener?.invoke()
+            true
+          }
           KeyEvent.KEYCODE_TAB          -> {
             if (!event.isAltPressed && !event.isCtrlPressed) {
               autoCompleteHelper.autoComplete(event.isShiftPressed)
@@ -159,7 +190,7 @@ class EditorHelper(
           else                          -> false
         }
       } else if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) {
-        !(event?.isShiftPressed ?: false)
+        !event.isShiftPressed
       } else {
         false
       }
@@ -191,6 +222,14 @@ class EditorHelper(
     this.enterListener = listener
   }
 
+  fun setOnUpListener(listener: (() -> Unit)?) {
+    this.upListener = listener
+  }
+
+  fun setOnDownListener(listener: (() -> Unit)?) {
+    this.downListener = listener
+  }
+
   fun setMultiLine(enabled: Boolean) = editText.setMultiLine(enabled)
 
   fun replaceText(text: CharSequence?) = editText.replaceText(text)
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt
index d3c582ce27319e2d81339595504c5ec79401b81c..4ff17b287eefd9e98e9dc320843e364bfee6d70b 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt
@@ -35,6 +35,8 @@ open class ChatViewModel : QuasselViewModel() {
   val bufferId = BehaviorSubject.createDefault(BufferId.MAX_VALUE)
   val bufferViewConfigId = BehaviorSubject.createDefault(-1)
   val recentlySentMessages = BehaviorSubject.createDefault(emptyList<CharSequence>())
+  val recentlySentMessageIndex = BehaviorSubject.createDefault(-1)
+  val inputCache = BehaviorSubject.createDefault<CharSequence>("")
   val showHidden = BehaviorSubject.createDefault(false)
   val bufferSearchTemporarilyVisible = BehaviorSubject.createDefault(false)
   val expandedNetworks = BehaviorSubject.createDefault(emptyMap<NetworkId, Boolean>())
@@ -145,6 +147,40 @@ open class ChatViewModel : QuasselViewModel() {
     )
   }
 
+  private fun recentMessagesChange(value: Int) {
+    val current = recentlySentMessageIndex.safeValue
+    val size = recentlySentMessages.safeValue.size
+    val nextValue = current + value
+    recentlySentMessageIndex.onNext(
+      if (nextValue < 0) -1
+      else (size + current + value) % size
+    )
+  }
+
+  fun recentMessagesValue() =
+    if (recentlySentMessageIndex.safeValue == -1) inputCache.safeValue
+    else recentlySentMessages.safeValue[recentlySentMessageIndex.safeValue]
+
+  fun recentMessagesIndexDown(content: CharSequence): CharSequence {
+    if (recentlySentMessageIndex.safeValue == -1) {
+      inputCache.onNext(content)
+    }
+    recentMessagesChange(+1)
+    return recentMessagesValue()
+  }
+
+  fun recentMessagesIndexUp(): CharSequence? {
+    if (recentlySentMessageIndex.safeValue > -1) {
+      recentMessagesChange(-1)
+    }
+    return recentMessagesValue()
+  }
+
+  fun recentMessagesIndexReset(): CharSequence? {
+    recentlySentMessageIndex.onNext(-1)
+    return recentMessagesValue()
+  }
+
   companion object {
     const val KEY_SELECTED_MESSAGES = "model_chat_selectedMessages"
     const val KEY_BUFFER_SEARCH = "model_chat_bufferSearch"