Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Volume buttons: implement Weechat-like input history rather than sent history #557

Merged
merged 1 commit into from
Feb 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ class BufferFragment : Fragment(), BufferEye {
savedInstanceState?.let {
restoreRecyclerViewState(it)
restoreSearchState(it)
restoreHistoryState(it)
}
}

Expand Down Expand Up @@ -281,7 +280,6 @@ class BufferFragment : Fragment(), BufferEye {
super.onSaveInstanceState(outState)
saveRecyclerViewState(outState)
saveSearchState(outState)
saveHistoryState(outState)
}

////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -518,15 +516,15 @@ class BufferFragment : Fragment(), BufferEye {
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP -> {
val up = keyCode == KeyEvent.KEYCODE_VOLUME_UP
when (P.volumeRole) {
P.VolumeRole.TEXT_SIZE -> {
P.VolumeRole.ChangeTextSize -> {
val change = if (up) 1f else -1f
val textSize = (P.textSize + change).coerceIn(5f, 30f)
P.setTextSizeColorAndLetterWidth(textSize)
return@OnKeyListener true
}
P.VolumeRole.SEND_HISTORY -> {
ulet(ui?.chatInput) {
history.navigateOffset(it, if (up) 1 else -1)
P.VolumeRole.NavigateInputHistory -> {
ui?.chatInput?.let {
P.history.navigate(it, if (up) History.Direction.Older else History.Direction.Newer)
}
return@OnKeyListener true
}
Expand All @@ -550,6 +548,7 @@ class BufferFragment : Fragment(), BufferEye {
assertThat(buffer).isEqualTo(linesAdapter?.buffer)
if (connectedToRelayAndSynced) {
SendMessageEvent.fireInput(buffer, input.text.toString())
P.history.reset(input)
input.setText("") // this will reset tab completion
} else {
Toaster.ErrorToast.show(R.string.error__etc__not_connected)
Expand Down Expand Up @@ -956,20 +955,6 @@ class BufferFragment : Fragment(), BufferEye {
}
}

////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// history
////////////////////////////////////////////////////////////////////////////////////////////////

private val history = History()

private fun saveHistoryState(outState: Bundle) {
outState.putBundle(KEY_HISTORY, history.save())
}

private fun restoreHistoryState(savedInstanceState: Bundle) {
savedInstanceState.getBundle(KEY_HISTORY)?.let { history.restore(it) }
}

////////////////////////////////////////////////////////////////////////////////////////////////

private fun setPendingInputForParallelFragments() = ulet(buffer, ui) { buffer, ui ->
Expand Down
36 changes: 31 additions & 5 deletions app/src/main/java/com/ubergeek42/WeechatAndroid/service/P.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.ubergeek42.WeechatAndroid.relay.BufferList;
import com.ubergeek42.WeechatAndroid.upload.UploadConfigKt;
import com.ubergeek42.WeechatAndroid.utils.Constants;
import com.ubergeek42.WeechatAndroid.utils.History;
import com.ubergeek42.WeechatAndroid.utils.MigratePreferences;
import com.ubergeek42.WeechatAndroid.utils.ThemeFix;
import com.ubergeek42.WeechatAndroid.utils.Utils;
Expand Down Expand Up @@ -125,7 +126,25 @@ public static void storeThemeOrColorSchemeColors(Context context) {

public static boolean showSend, showTab, showPaperclip, hotlistSync;

public enum VolumeRole {NONE, TEXT_SIZE, SEND_HISTORY};
public enum VolumeRole {
DoNothing("none"),
ChangeTextSize("change_text_size"),
NavigateInputHistory("navigate_input_history");

public final String value;

VolumeRole(String value) {
this.value = value;
}

static VolumeRole fromString(String value) {
for (VolumeRole role : VolumeRole.values()) {
if (role.value.equals(value)) return role;
}
// Should never be reached.
return VolumeRole.ChangeTextSize;
}
}
public static VolumeRole volumeRole;

public static boolean showBufferFilter;
Expand Down Expand Up @@ -170,7 +189,7 @@ public enum VolumeRole {NONE, TEXT_SIZE, SEND_HISTORY};
showTab = p.getBoolean(PREF_SHOW_TAB, PREF_SHOW_TAB_D);
showPaperclip = p.getBoolean(PREF_SHOW_PAPERCLIP, PREF_SHOW_PAPERCLIP_D);
hotlistSync = p.getBoolean(PREF_HOTLIST_SYNC, PREF_HOTLIST_SYNC_D);
volumeRole = VolumeRole.values()[Integer.parseInt(p.getString(PREF_VOLUME_ROLE, PREF_VOLUME_ROLE_D))];
volumeRole = VolumeRole.fromString(p.getString(PREF_VOLUME_ROLE, PREF_VOLUME_ROLE_D));

// buffer list filter
showBufferFilter = p.getBoolean(PREF_SHOW_BUFFER_FILTER, PREF_SHOW_BUFFER_FILTER_D);
Expand Down Expand Up @@ -368,7 +387,7 @@ public static void loadServerKeyVerifier() {
case PREF_SHOW_TAB: showTab = p.getBoolean(key, PREF_SHOW_TAB_D); break;
case PREF_SHOW_PAPERCLIP: showPaperclip = p.getBoolean(key, PREF_SHOW_PAPERCLIP_D); break;
case PREF_HOTLIST_SYNC: hotlistSync = p.getBoolean(key, PREF_HOTLIST_SYNC_D); break;
case PREF_VOLUME_ROLE: volumeRole = VolumeRole.values()[Integer.parseInt(p.getString(PREF_VOLUME_ROLE, PREF_VOLUME_ROLE_D))]; break;
case PREF_VOLUME_ROLE: volumeRole = VolumeRole.fromString(p.getString(PREF_VOLUME_ROLE, PREF_VOLUME_ROLE_D)); break;

// buffer list fragment
case PREF_SHOW_BUFFER_FILTER: showBufferFilter = p.getBoolean(key, PREF_SHOW_BUFFER_FILTER_D); break;
Expand Down Expand Up @@ -439,11 +458,11 @@ static void setServiceAlive(boolean alive) {

// protocol must be changed each time anything that uses the following function changes
// needed to make sure nothing crashes if we cannot restore the data
private static final int PROTOCOL_ID = 18;
private static final int PROTOCOL_ID = 19;

@AnyThread @Cat public static void saveStuff() {
for (Buffer buffer : BufferList.buffers) saveLastReadLine(buffer);
String data = Utils.serialize(new Object[]{openBuffers, bufferToLastReadLine, sentMessages});
String data = Utils.serialize(new Object[]{openBuffers, bufferToLastReadLine, sentMessages, history});
p.edit().putString(PREF_DATA, data).putInt(PREF_PROTOCOL_ID, PROTOCOL_ID).apply();
}

Expand All @@ -456,6 +475,7 @@ static void setServiceAlive(boolean alive) {
if (array[0] instanceof LinkedHashSet) openBuffers = (LinkedHashSet<Long>) array[0];
if (array[1] instanceof LinkedHashMap) bufferToLastReadLine = (LinkedHashMap<Long, BufferHotData>) array[1];
if (array[2] instanceof LinkedList) sentMessages = (LinkedList<String>) array[2];
if (array[3] instanceof History) history = (History) array[3];
}

////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -527,6 +547,12 @@ static void addSentMessage(String line) {
sentMessages.pop();
}

////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// input history
////////////////////////////////////////////////////////////////////////////////////////////////

public static History history = new History();

////////////////////////////////////////////////////////////////////////////////////////////////

private static String getString(String key, String defValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public class Constants {
public final static String PREF_SHOW_TAB = "tabbtn_show";
final public static boolean PREF_SHOW_TAB_D = true;
public final static String PREF_VOLUME_ROLE = "buttons__volume";
final public static String PREF_VOLUME_ROLE_D = String.valueOf(P.VolumeRole.TEXT_SIZE.ordinal());
final public static String PREF_VOLUME_ROLE_D = P.VolumeRole.ChangeTextSize.value;

final public static String PREF_SHOW_PAPERCLIP = "buttons__show_paperclip";
final public static boolean PREF_SHOW_PAPERCLIP_D = true;
Expand Down
145 changes: 101 additions & 44 deletions app/src/main/java/com/ubergeek42/WeechatAndroid/utils/History.kt
Original file line number Diff line number Diff line change
@@ -1,54 +1,111 @@
package com.ubergeek42.WeechatAndroid.utils

import android.os.Bundle
import android.text.Editable
import android.os.Parcel
import android.widget.EditText
import androidx.core.os.bundleOf
import com.ubergeek42.WeechatAndroid.service.P

class History {
private var index = -1
private var userInput: Editable? = null
private var selectionStart: Int? = null
private var selectionEnd: Int? = null

fun navigateOffset(editText: EditText, offset: Int) {
if (index == -1) {
userInput = editText.text
selectionStart = editText.selectionStart
selectionEnd = editText.selectionEnd
}
val newIndex = index + offset
messageAt(newIndex)?.let {
editText.text = it
editText.post {
if (newIndex == -1) {
// Restore user selection.
editText.setSelection(selectionStart ?: 0, selectionEnd ?: editText.length())
} else {
// Go to end for sent messages.
editText.setSelection(editText.length())
}
import androidx.core.text.getSpans
import com.ubergeek42.WeechatAndroid.R
import com.ubergeek42.WeechatAndroid.upload.ShareSpan

class History : java.io.Serializable {
companion object {
// Sentinel index representing the most recent end of the message history.
private const val END = -1

private const val MAX_MESSAGE_LENGTH = 2000
private const val MAX_HISTORY_LENGTH = 40
}

class Message : java.io.Serializable {
lateinit var content: String
var selectionStart: Int = 0
var selectionEnd: Int = 0

constructor(editText: EditText) {
updateFromEdit(editText)
}

constructor(source: Parcel) {
content = source.readString() ?: ""
selectionStart = source.readInt()
selectionStart = source.readInt()
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be easier to read

val newIndex = when (direction) {
    Direction.Older -> sentMessageIndex + 1
    Direction.Newer -> sentMessageIndex - 1
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.



fun updateFromEdit(editText: EditText) {
content = Utils.cut(editText.text.toString(), MAX_MESSAGE_LENGTH)
// Input length might be shorter because of the text ellipsis from above.
selectionStart = minOf(content.length, editText.selectionStart)
selectionEnd = minOf(content.length, editText.selectionEnd)
}

fun applyToEdit(editText: EditText) {
editText.setText(content)
editText.setSelection(selectionStart, selectionEnd)
}
}

private var messages: MutableList<Message> = mutableListOf()
private var index = END

enum class Direction { Older, Newer }

// Save message iff it is not empty and not equal to the most recent message.
// Will remove the oldest message if the message size goes above the maximum history size.
private fun maybeSaveMessage(editText: EditText): Boolean {
val message = Message(editText)
val content = message.content
if (content.trim().isNotEmpty()) {
val last = messages.getOrNull(0)?.content
if (last?.equalsIgnoringUselessSpans(content) != true) {
messages.add(0, message)
if (messages.size > MAX_HISTORY_LENGTH) messages.removeLast()
return true
}
index = newIndex
}
return false
}

fun save(): Bundle = bundleOf(
Pair("index", index),
Pair("userInput", userInput),
Pair("selStart", selectionStart),
Pair("selEnd", selectionEnd),
)

fun restore(bundle: Bundle) {
index = bundle.getInt("index", -1)
userInput = bundle.get("userInput") as Editable?
selectionStart = bundle.getInt("selStart", -1).let { if (it == -1) null else it }
selectionEnd = bundle.getInt("selEnd", -1).let { if (it == -1) null else it }
private fun shareSpanCount(editText: EditText): Int = editText.text.getSpans<ShareSpan>().size

fun navigate(editText: EditText, direction: Direction) {
val spc = shareSpanCount(editText)
if (spc > 0) {
// The editText contains non-uploaded ShareSpans that would be lost by navigating away.
// Bail out early to prevent data loss.
Toaster.ErrorToast.show(
editText.context.resources.getQuantityText(
R.plurals.error__history_with_sharespans,
spc
).toString()
)
return
}
var newIndex = when (direction) {
Direction.Older -> index + 1
Direction.Newer -> index - 1
}
if (index == END) {
// Not in history yet. Push current edit to history.
val wasInserted = maybeSaveMessage(editText)
// Special case when saving from end and going older: since we've just inserted, we need
// to skip over the message that was just saved.
if (wasInserted && direction == Direction.Older) newIndex += 1
} else {
// In history already. Save changes before navigating away.
messages[index].updateFromEdit(editText)
}
if (newIndex < END) newIndex = END
if (newIndex >= messages.size) newIndex = messages.size - 1
if (newIndex == END) {
if (editText.text.isNotEmpty()) editText.setText("")
} else if (newIndex != index) {
messages[newIndex].applyToEdit(editText)
}
index = newIndex
}

private fun messageAt(index: Int): Editable? =
if (index == -1) userInput else P.sentMessages.getOrNull(P.sentMessages.size - 1 - index)
?.let { Editable.Factory.getInstance().newEditable(it) }
fun reset(editText: EditText) {
maybeSaveMessage(editText)
index = END
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ class MigratePreferences(val context: Context) {
val volumeChangesSize = preferences.getBoolean(Constants.Deprecated.PREF_VOLUME_BTN_SIZE, Constants.Deprecated.PREF_VOLUME_BTN_SIZE_D)
preferences.edit {
this.remove(Constants.Deprecated.PREF_VOLUME_BTN_SIZE)
this.putString(Constants.PREF_VOLUME_ROLE, (if (volumeChangesSize) VolumeRole.TEXT_SIZE else VolumeRole.NONE).ordinal.toString(10))
this.putString(
Constants.PREF_VOLUME_ROLE,
(if (volumeChangesSize) VolumeRole.ChangeTextSize else VolumeRole.DoNothing).value
)
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -703,9 +703,10 @@
wenn der Büroklammer-Taste versteckt wurde,
um mehr Platz für das Eingabefeld zu schaffen.</string>

<string name="pref_buttons_volume__actions__none">Nichts tun</string>
<string name="pref_buttons_volume__actions__text_size">Lautstärketasten ändern die Textgröße</string>
<string name="pref_buttons_volume__actions__history">Navigieren im Sendeverlauf</string>
<string name="pref__buttons__volume__title">Lautstärketasten</string>
<string name="pref__buttons__volume__actions__none">Nichts tun</string>
<string name="pref__buttons__volume__actions__change_text_size">Lautstärketasten ändern die Textgröße</string>
<string name="pref__buttons__volume__actions__navigate_input_history">Navigieren Sie durch den Eingabeverlauf</string>

<!-- ####################################################################################### -->
<!-- #################################### notifications #################################### -->
Expand Down
14 changes: 11 additions & 3 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
<string name="error__etc__cannot_share_to_empty_buffer_list">Aucun tampon disponible pour partager</string>
<string name="error__etc__activity_not_found_for_url">Activité introuvable pour “l’intent” %s</string>

<plurals name="error__history_with_sharespans">
<item quantity="one" tools:ignore="ImpliedQuantity">Supprimez ou uploadez l’image pour naviguer</item>
<item quantity="other">Supprimez ou uploadez les images pour naviguer</item>
<item quantity="many">Supprimez ou uploadez les images pour naviguer</item>
</plurals>

<!-- ############################## main activity ui elements ############################## -->

<string name="ui__button_paperclip">Téléverser un fichier</string>
Expand Down Expand Up @@ -691,9 +697,11 @@
Quand le bouton «&#8239;trombone&#8239;» devient invisible pour laisser de la place au champ de saisie,
il reste possible de joindre des fichiers via le menu déroulant.</string>

<string name="pref_buttons_volume__actions__none">Ne rien faire</string>
<string name="pref_buttons_volume__actions__text_size">Changer la taille du texte</string>
<string name="pref_buttons_volume__actions__history">Naviguer dans les messages envoyés</string>
<string name="pref__buttons__volume__title">Les boutons de volume</string>
zopieux marked this conversation as resolved.
Show resolved Hide resolved
<string name="pref__buttons__volume__actions__none">Ne font rien de spécial</string>
<string name="pref__buttons__volume__actions__change_text_size">Changent la taille du texte</string>
<string name="pref__buttons__volume__actions__navigate_input_history">Naviguent dans l’historique de saisie</string>

<!-- ####################################################################################### -->
<!-- #################################### notifications #################################### -->
<!-- ####################################################################################### -->
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,10 @@
Когда кнопка «Скрепка» скрыта, чтобы оставить больше места для поля ввода,
выбирать файлы можно через меню.</string>

<string name="pref_buttons_volume__actions__none">Ничего не делать</string>
<string name="pref_buttons_volume__actions__text_size">Кнопки громкости меняют размер текста</string>
<string name="pref_buttons_volume__actions__history">Навигация по истории отправлений</string>
<string name="pref__buttons__volume__title">Кнопки громкости</string>
<string name="pref__buttons__volume__actions__none">Ничего не делать</string>
<string name="pref__buttons__volume__actions__change_text_size">Кнопки громкости меняют размер текста</string>
<string name="pref__buttons__volume__actions__navigate_input_history">Навигация по истории ввода</string>

<!-- ####################################################################################### -->
<!-- #################################### notifications #################################### -->
Expand Down
Loading