From 37ade3a709dbbb0c07d983d800c50b1c90a7d840 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Wed, 7 Mar 2018 14:44:12 +0100 Subject: [PATCH] Implement formatting editor --- .../quasseldroid_ng/ui/chat/ChatActivity.kt | 45 +++- .../quasseldroid_ng/ui/chat/InputEditor.kt | 207 ++++++++++++++++++ .../util/helper/SelectionHelper.kt | 10 + .../util/irc/format/IrcFormatDeserializer.kt | 87 +++----- .../util/irc/format/IrcFormatSerializer.kt | 7 +- ...rmat_fill.xml => ic_format_background.xml} | 0 app/src/main/res/drawable/ic_format_clear.xml | 10 + ...mat_paint.xml => ic_format_foreground.xml} | 0 .../main/res/drawable/ic_format_monospace.xml | 10 + .../res/drawable/ic_format_strikethrough.xml | 10 + .../res/layout-sw720dp-land/activity_main.xml | 2 +- app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/layout_editor.xml | 5 +- app/src/main/res/layout/layout_slider.xml | 1 + app/src/main/res/layout/layout_toolbar.xml | 2 +- app/src/main/res/menu/editor.xml | 46 ++++ app/src/main/res/menu/input_panel.xml | 9 + app/src/main/res/values/strings.xml | 8 + .../primitive/serializer/StringSerializer.kt | 4 +- 19 files changed, 387 insertions(+), 78 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/InputEditor.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/SelectionHelper.kt rename app/src/main/res/drawable/{ic_format_fill.xml => ic_format_background.xml} (100%) create mode 100644 app/src/main/res/drawable/ic_format_clear.xml rename app/src/main/res/drawable/{ic_format_paint.xml => ic_format_foreground.xml} (100%) create mode 100644 app/src/main/res/drawable/ic_format_monospace.xml create mode 100644 app/src/main/res/drawable/ic_format_strikethrough.xml create mode 100644 app/src/main/res/menu/editor.xml create mode 100644 app/src/main/res/menu/input_panel.xml diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt index df74b7053..af0bf9cb1 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt @@ -11,11 +11,12 @@ import android.os.Build import android.os.Bundle import android.os.PersistableBundle import android.support.design.widget.Snackbar +import android.support.v4.graphics.drawable.DrawableCompat import android.support.v4.widget.DrawerLayout import android.support.v7.app.ActionBarDrawerToggle +import android.support.v7.widget.ActionMenuView import android.support.v7.widget.Toolbar import android.text.InputType -import android.text.Spanned import android.view.* import android.view.inputmethod.EditorInfo import android.widget.EditText @@ -40,23 +41,29 @@ import de.kuschku.quasseldroid_ng.ui.settings.data.Settings import de.kuschku.quasseldroid_ng.ui.viewmodel.QuasselViewModel import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread import de.kuschku.quasseldroid_ng.util.helper.* -import de.kuschku.quasseldroid_ng.util.irc.format.IrcFormatSerializer import de.kuschku.quasseldroid_ng.util.service.ServiceBoundActivity import de.kuschku.quasseldroid_ng.util.ui.MaterialContentLoadingProgressBar -class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenceChangeListener { - @BindView(R.id.drawerLayout) +class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenceChangeListener, + ActionMenuView.OnMenuItemClickListener { + @BindView(R.id.drawer_layout) lateinit var drawerLayout: DrawerLayout @BindView(R.id.toolbar) lateinit var toolbar: Toolbar - @BindView(R.id.progressBar) + @BindView(R.id.formatting_menu) + lateinit var formattingMenu: ActionMenuView + + @BindView(R.id.progress_bar) lateinit var progressBar: MaterialContentLoadingProgressBar @BindView(R.id.editor_panel) lateinit var editorPanel: SlidingUpPanelLayout + @BindView(R.id.history_panel) + lateinit var historyPanel: SlidingUpPanelLayout + @BindView(R.id.send) lateinit var send: ImageButton @@ -75,7 +82,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc private lateinit var backlogSettings: BacklogSettings - private lateinit var ircFormatSerializer: IrcFormatSerializer + private lateinit var inputEditor: InputEditor private val panelSlideListener: SlidingUpPanelLayout.PanelSlideListener = object : SlidingUpPanelLayout.PanelSlideListener { @@ -107,7 +114,21 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc viewModel = ViewModelProviders.of(this)[QuasselViewModel::class.java] viewModel.setBackend(this.backend) backlogSettings = Settings.backlog(this) - ircFormatSerializer = IrcFormatSerializer(this) + + inputEditor = InputEditor(chatline) + menuInflater.inflate(inputEditor.menu, formattingMenu.menu) + menuInflater.inflate(R.menu.input_panel, formattingMenu.menu) + formattingMenu.setOnMenuItemClickListener(this) + + formattingMenu.context.theme.styledAttributes(R.attr.colorControlNormal) { + val color = getColor(0, 0) + + for (item in (0 until formattingMenu.menu.size()).map { formattingMenu.menu.getItem(it) }) { + val drawable = item.icon.mutate() + DrawableCompat.setTint(drawable, color) + item.icon = drawable + } + } database = QuasselDatabase.Creator.init(application) @@ -185,7 +206,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc for (line in text.lineSequence()) { session.aliasManager?.processInput( bufferInfo, - if (line is Spanned) ircFormatSerializer.toEscapeCodes(line) else line.toString(), + inputEditor.formattedString, output ) } @@ -320,6 +341,14 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc else -> super.onOptionsItemSelected(item) } + override fun onMenuItemClick(item: MenuItem?) = when (item?.itemId) { + R.id.input_history -> { + historyPanel.panelState = SlidingUpPanelLayout.PanelState.EXPANDED + true + } + else -> inputEditor.onMenuItemClick(item) + } + override fun onDestroy() { handler.onDestroy() super.onDestroy() diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/InputEditor.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/InputEditor.kt new file mode 100644 index 000000000..ebf820700 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/InputEditor.kt @@ -0,0 +1,207 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +import android.graphics.Typeface +import android.support.annotation.MenuRes +import android.text.Editable +import android.text.Spanned +import android.text.style.StrikethroughSpan +import android.text.style.StyleSpan +import android.text.style.TypefaceSpan +import android.text.style.UnderlineSpan +import android.view.MenuItem +import android.widget.EditText +import de.kuschku.quasseldroid_ng.R +import de.kuschku.quasseldroid_ng.util.helper.selection +import de.kuschku.quasseldroid_ng.util.irc.format.IrcFormatSerializer +import de.kuschku.quasseldroid_ng.util.irc.format.spans.* + +class InputEditor(private val editText: EditText) { + private val serializer = IrcFormatSerializer(editText.context) + val formattedString: String + get() = serializer.toEscapeCodes(editText.text) + + @MenuRes + val menu: Int = R.menu.editor + + fun toggleBold(range: IntRange, createNew: Boolean = true) { + if (range.isEmpty()) + return + + val exists = editText.text.removeSpans<StyleSpan, IrcBoldSpan>(range) { span -> + when { + span is IrcBoldSpan -> span + span.style == Typeface.BOLD -> IrcBoldSpan() + else -> null + } + } + + if (!exists && createNew) { + editText.text.setSpan( + IrcBoldSpan(), range.start, range.endInclusive + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) + } + } + + fun toggleItalic(range: IntRange, createNew: Boolean = true) { + if (range.isEmpty()) + return + + val exists = editText.text.removeSpans<StyleSpan, IrcItalicSpan>(range) { span -> + when { + span is IrcItalicSpan -> span + span.style == Typeface.ITALIC -> IrcItalicSpan() + else -> null + } + } + + if (!exists && createNew) { + editText.text.setSpan( + IrcItalicSpan(), range.start, range.endInclusive + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) + } + } + + fun toggleUnderline(range: IntRange, createNew: Boolean = true) { + if (range.isEmpty()) + return + + val exists = editText.text.removeSpans<UnderlineSpan, IrcUnderlineSpan>(range) { span -> + when { + span is IrcUnderlineSpan -> span + else -> IrcUnderlineSpan() + } + } + + if (!exists && createNew) { + editText.text.setSpan( + IrcUnderlineSpan(), range.start, range.endInclusive + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) + } + } + + fun toggleStrikethrough(range: IntRange, createNew: Boolean = true) { + if (range.isEmpty()) + return + + val exists = editText.text.removeSpans<StrikethroughSpan, IrcStrikethroughSpan>(range) { span -> + when { + span is IrcStrikethroughSpan -> span + else -> IrcStrikethroughSpan() + } + } + + if (!exists && createNew) { + editText.text.setSpan( + IrcStrikethroughSpan(), range.start, range.endInclusive + 1, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) + } + } + + fun toggleMonospace(range: IntRange, createNew: Boolean = true) { + if (range.isEmpty()) + return + + val exists = editText.text.removeSpans<TypefaceSpan, IrcMonospaceSpan>(range) { span -> + when { + span is IrcMonospaceSpan -> span + span.family == "monospace" -> IrcMonospaceSpan() + else -> null + } + } + + if (!exists && createNew) { + editText.text.setSpan( + IrcMonospaceSpan(), range.start, range.endInclusive + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) + } + } + + fun clearFormatting(range: IntRange) { + if (range.isEmpty()) + return + + toggleBold(range, false) + toggleItalic(range, false) + toggleUnderline(range, false) + toggleStrikethrough(range, false) + toggleMonospace(range, false) + } + + fun onMenuItemClick(item: MenuItem?) = when (item?.itemId) { + R.id.format_bold -> { + toggleBold(editText.selection) + true + } + R.id.format_italic -> { + toggleItalic(editText.selection) + true + } + R.id.format_underline -> { + toggleUnderline(editText.selection) + true + } + R.id.format_strikethrough -> { + toggleStrikethrough(editText.selection) + true + } + R.id.format_monospace -> { + toggleMonospace(editText.selection) + true + } + R.id.format_clear -> { + clearFormatting(editText.selection) + true + } + else -> false + } + + private inline fun <reified U, T> Editable.removeSpans( + range: IntRange, removeInvalid: Boolean = false, f: (U) -> T?): Boolean where T : Copyable<T> { + if (range.isEmpty()) + return false + + var removedAny = false + + for (raw in getSpans<U>(range.start, range.endInclusive + 1, U::class.java)) { + val spanFlags = getSpanFlags(raw) + if (spanFlags and Spanned.SPAN_COMPOSING != 0) continue + + val spanEnd = getSpanEnd(raw) + val spanStart = getSpanStart(raw) + + val span = f(raw) + if (span == null) { + if (removeInvalid) + removeSpan(raw) + } else { + removeSpan(raw) + + val endIsIn = spanEnd in range + val endIsAfter = spanEnd > range.endInclusive + 1 + + val startIsIn = spanStart in range + val startIsBefore = spanStart < range.start + + if (endIsIn && startIsIn) { + removedAny = true + } else if (endIsIn) { + setSpan(span, spanStart, range.start, spanFlags) + removedAny = true + } else if (startIsIn) { + setSpan(span, range.endInclusive + 1, spanEnd, spanFlags) + removedAny = true + } else if (startIsBefore && endIsAfter) { + setSpan(span, spanStart, range.start, spanFlags) + setSpan(span.copy(), range.endInclusive + 1, spanEnd, spanFlags) + removedAny = true + } else if (startIsBefore) { + setSpan(span, spanStart, range.start, spanFlags) + removedAny = true + } + } + } + + return removedAny + } +} \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/SelectionHelper.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/SelectionHelper.kt new file mode 100644 index 000000000..f69883b38 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/SelectionHelper.kt @@ -0,0 +1,10 @@ +package de.kuschku.quasseldroid_ng.util.helper + +import android.text.Selection +import android.widget.EditText + +val CharSequence.selection: IntRange + get() = Selection.getSelectionStart(this) until Selection.getSelectionEnd(this) + +val EditText.selection: IntRange + get() = text.selection \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatDeserializer.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatDeserializer.kt index 75d3c7c00..1033d005b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatDeserializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatDeserializer.kt @@ -76,13 +76,13 @@ class IrcFormatDeserializer(private val context: Context) { if (str == null) return "" val plainText = SpannableStringBuilder() - var bold: FormatDescription? = null - var italic: FormatDescription? = null - var underline: FormatDescription? = null - var strikethrough: FormatDescription? = null - var monospace: FormatDescription? = null - var color: FormatDescription? = null - var hexColor: FormatDescription? = null + var bold: FormatDescription<BoldIrcFormat>? = null + var italic: FormatDescription<ItalicIrcFormat>? = null + var underline: FormatDescription<UnderlineIrcFormat>? = null + var strikethrough: FormatDescription<StrikethroughIrcFormat>? = null + var monospace: FormatDescription<MonospaceIrcFormat>? = null + var color: FormatDescription<ColorIrcFormat>? = null + var hexColor: FormatDescription<HexIrcFormat>? = null // Iterating over every character var normalCount = 0 @@ -100,8 +100,7 @@ class IrcFormatDeserializer(private val context: Context) { bold = null // Otherwise create a new one } else { - val format = fromId(character) - bold = FormatDescription(plainText.length, format!!) + bold = FormatDescription(plainText.length, BoldIrcFormat()) } } CODE_ITALIC -> { @@ -114,8 +113,7 @@ class IrcFormatDeserializer(private val context: Context) { italic = null // Otherwise create a new one } else { - val format = fromId(character) - italic = FormatDescription(plainText.length, format!!) + italic = FormatDescription(plainText.length, ItalicIrcFormat()) } } CODE_UNDERLINE -> { @@ -128,8 +126,7 @@ class IrcFormatDeserializer(private val context: Context) { underline = null // Otherwise create a new one } else { - val format = fromId(character) - underline = FormatDescription(plainText.length, format!!) + underline = FormatDescription(plainText.length, UnderlineIrcFormat()) } } CODE_STRIKETHROUGH -> { @@ -142,8 +139,7 @@ class IrcFormatDeserializer(private val context: Context) { strikethrough = null // Otherwise create a new one } else { - val format = fromId(character) - strikethrough = FormatDescription(plainText.length, format!!) + strikethrough = FormatDescription(plainText.length, StrikethroughIrcFormat()) } } CODE_MONOSPACE -> { @@ -156,8 +152,7 @@ class IrcFormatDeserializer(private val context: Context) { monospace = null // Otherwise create a new one } else { - val format = fromId(character) - monospace = FormatDescription(plainText.length, format!!) + monospace = FormatDescription(plainText.length, MonospaceIrcFormat()) } } CODE_COLOR -> { @@ -183,7 +178,7 @@ class IrcFormatDeserializer(private val context: Context) { if (colorize) color.apply(plainText, plainText.length) // Reuse old background, if possible if (background.toInt() == -1) - background = (color.format as ColorIrcFormat).background + background = color.format.background } // Add new format color = FormatDescription(plainText.length, ColorIrcFormat(foreground, background)) @@ -207,7 +202,7 @@ class IrcFormatDeserializer(private val context: Context) { if (colorEnd > colorStart) { val foreground = readHexNumber(str, colorStart, colorEnd) // Add new format - hexColor = FormatDescription(plainText.length, ColorHexFormat(foreground)) + hexColor = FormatDescription(plainText.length, HexIrcFormat(foreground)) // i points in front of the next character i = colorEnd - 1 @@ -226,7 +221,7 @@ class IrcFormatDeserializer(private val context: Context) { if (color != null) { if (colorize) color.apply(plainText, plainText.length) color = FormatDescription( - plainText.length, (color.format as ColorIrcFormat).copySwapped() + plainText.length, color.format.copySwapped() ) } } @@ -274,20 +269,27 @@ class IrcFormatDeserializer(private val context: Context) { if (underline != null) { if (colorize) underline.apply(plainText, plainText.length) } + if (strikethrough != null) { + if (colorize) strikethrough.apply(plainText, plainText.length) + } + if (monospace != null) { + if (colorize) monospace.apply(plainText, plainText.length) + } if (color != null) { if (colorize) color.apply(plainText, plainText.length) } + if (hexColor != null) { + if (colorize) hexColor.apply(plainText, plainText.length) + } plainText.append(str.substring(str.length - normalCount, str.length)) return plainText } private interface IrcFormat { fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) - - fun id(): Byte } - private class FormatDescription(val start: Int, val format: IrcFormat) { + private class FormatDescription<U : IrcFormat>(val start: Int, val format: U) { fun apply(editable: SpannableStringBuilder, end: Int) { format.applyTo(editable, start, end) @@ -298,53 +300,33 @@ class IrcFormatDeserializer(private val context: Context) { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { editable.setSpan(IrcItalicSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } - - override fun id(): Byte { - return CODE_ITALIC.toByte() - } } private class UnderlineIrcFormat : IrcFormat { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { editable.setSpan(IrcUnderlineSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } - - override fun id(): Byte { - return CODE_UNDERLINE.toByte() - } } private class StrikethroughIrcFormat : IrcFormat { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { editable.setSpan(IrcStrikethroughSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } - - override fun id(): Byte { - return CODE_STRIKETHROUGH.toByte() - } } private class MonospaceIrcFormat : IrcFormat { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { editable.setSpan(IrcMonospaceSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } - - override fun id(): Byte { - return CODE_MONOSPACE.toByte() - } } private class BoldIrcFormat : IrcFormat { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { editable.setSpan(IrcBoldSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } - - override fun id(): Byte { - return CODE_BOLD.toByte() - } } - private inner class ColorHexFormat(val color: Int) : IrcFormat { + private inner class HexIrcFormat(val color: Int) : IrcFormat { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { editable.setSpan( @@ -352,10 +334,6 @@ class IrcFormatDeserializer(private val context: Context) { Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) } - - override fun id(): Byte { - return CODE_HEXCOLOR.toByte() - } } private inner class ColorIrcFormat(val foreground: Byte, val background: Byte) : IrcFormat { @@ -378,10 +356,6 @@ class IrcFormatDeserializer(private val context: Context) { fun copySwapped(): ColorIrcFormat { return ColorIrcFormat(background, foreground) } - - override fun id(): Byte { - return CODE_COLOR.toByte() - } } companion object { @@ -465,14 +439,5 @@ class IrcFormatDeserializer(private val context: Context) { } return start + i } - - private fun fromId(id: Char) = when (id) { - CODE_BOLD -> BoldIrcFormat() - CODE_ITALIC -> ItalicIrcFormat() - CODE_UNDERLINE -> UnderlineIrcFormat() - CODE_STRIKETHROUGH -> StrikethroughIrcFormat() - CODE_MONOSPACE -> MonospaceIrcFormat() - else -> null - } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatSerializer.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatSerializer.kt index ced8e7699..db0feed9c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatSerializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/irc/format/IrcFormatSerializer.kt @@ -105,6 +105,7 @@ class IrcFormatSerializer internal constructor(private val context: Context) { } fun writeReset() { + println("reset") out.append(CODE_RESET) } @@ -139,8 +140,8 @@ class IrcFormatSerializer internal constructor(private val context: Context) { when (aStyle) { is StyleSpan -> { - afterBold = (aStyle.style and Typeface.BOLD != 0) - afterItalic = (aStyle.style and Typeface.ITALIC != 0) + afterBold = afterBold || aStyle.style and Typeface.BOLD != 0 + afterItalic = afterItalic || aStyle.style and Typeface.ITALIC != 0 } is UnderlineSpan -> afterUnderline = true is StrikethroughSpan -> afterStrikethrough = true @@ -199,7 +200,7 @@ class IrcFormatSerializer internal constructor(private val context: Context) { i = next } - if (bold || italic || underline || background != null || foreground != null) + if (bold || italic || underline || strikethrough || monospace || background != null || foreground != null) writeReset() } diff --git a/app/src/main/res/drawable/ic_format_fill.xml b/app/src/main/res/drawable/ic_format_background.xml similarity index 100% rename from app/src/main/res/drawable/ic_format_fill.xml rename to app/src/main/res/drawable/ic_format_background.xml diff --git a/app/src/main/res/drawable/ic_format_clear.xml b/app/src/main/res/drawable/ic_format_clear.xml new file mode 100644 index 000000000..c29344e8e --- /dev/null +++ b/app/src/main/res/drawable/ic_format_clear.xml @@ -0,0 +1,10 @@ +<!-- drawable/format_clear.xml --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000" + android:pathData="M6,5V5.18L8.82,8H11.22L10.5,9.68L12.6,11.78L14.21,8H20V5H6M3.27,5L2,6.27L8.97,13.24L6.5,19H9.5L11.07,15.34L16.73,21L18,19.73L3.55,5.27L3.27,5Z" /> +</vector> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_format_paint.xml b/app/src/main/res/drawable/ic_format_foreground.xml similarity index 100% rename from app/src/main/res/drawable/ic_format_paint.xml rename to app/src/main/res/drawable/ic_format_foreground.xml diff --git a/app/src/main/res/drawable/ic_format_monospace.xml b/app/src/main/res/drawable/ic_format_monospace.xml new file mode 100644 index 000000000..b2574f467 --- /dev/null +++ b/app/src/main/res/drawable/ic_format_monospace.xml @@ -0,0 +1,10 @@ +<!-- drawable/code_tags.xml --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000" + android:pathData="M14.6,16.6L19.2,12L14.6,7.4L16,6L22,12L16,18L14.6,16.6M9.4,16.6L4.8,12L9.4,7.4L8,6L2,12L8,18L9.4,16.6Z" /> +</vector> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_format_strikethrough.xml b/app/src/main/res/drawable/ic_format_strikethrough.xml new file mode 100644 index 000000000..4a9ace4d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_format_strikethrough.xml @@ -0,0 +1,10 @@ +<!-- drawable/format_strikethrough_variant.xml --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#000" + android:pathData="M23,12V14H18.61C19.61,16.14 19.56,22 12.38,22C4.05,22.05 4.37,15.5 4.37,15.5L8.34,15.55C8.37,18.92 11.5,18.92 12.12,18.88C12.76,18.83 15.15,18.84 15.34,16.5C15.42,15.41 14.32,14.58 13.12,14H1V12H23M19.41,7.89L15.43,7.86C15.43,7.86 15.6,5.09 12.15,5.08C8.7,5.06 9,7.28 9,7.56C9.04,7.84 9.34,9.22 12,9.88H5.71C5.71,9.88 2.22,3.15 10.74,2C19.45,0.8 19.43,7.91 19.41,7.89Z" /> +</vector> \ No newline at end of file diff --git a/app/src/main/res/layout-sw720dp-land/activity_main.xml b/app/src/main/res/layout-sw720dp-land/activity_main.xml index cb1aeeba4..7af3ba7a1 100644 --- a/app/src/main/res/layout-sw720dp-land/activity_main.xml +++ b/app/src/main/res/layout-sw720dp-land/activity_main.xml @@ -1,7 +1,7 @@ <android.support.v4.widget.DrawerLayout 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/drawerLayout" + android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b7d6dc16c..6df810bd3 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,7 +1,7 @@ <android.support.v4.widget.DrawerLayout 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/drawerLayout" + android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> diff --git a/app/src/main/res/layout/layout_editor.xml b/app/src/main/res/layout/layout_editor.xml index f5ab7962b..a8bc035af 100644 --- a/app/src/main/res/layout/layout_editor.xml +++ b/app/src/main/res/layout/layout_editor.xml @@ -25,7 +25,7 @@ android:hint="@string/label_placeholder" android:imeOptions="flagNoExtractUi" android:inputType="textCapSentences|textAutoCorrect|textShortMessage" - android:minHeight="?actionBarSize" + android:minHeight="?attr/actionBarSize" android:paddingBottom="8dp" android:paddingLeft="20dp" android:paddingRight="20dp" @@ -53,6 +53,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorBackgroundCard" + android:theme="?attr/formatBarTheme" app:layout_constraintBottom_toBottomOf="parent"> <android.support.v7.widget.Toolbar @@ -62,7 +63,7 @@ <android.support.v7.widget.ActionMenuView android:id="@+id/formatting_menu" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" /> </android.support.v7.widget.Toolbar> diff --git a/app/src/main/res/layout/layout_slider.xml b/app/src/main/res/layout/layout_slider.xml index 6d4a3b7a4..d9459514e 100644 --- a/app/src/main/res/layout/layout_slider.xml +++ b/app/src/main/res/layout/layout_slider.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/history_panel" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="bottom" diff --git a/app/src/main/res/layout/layout_toolbar.xml b/app/src/main/res/layout/layout_toolbar.xml index ae3f32270..ff2a77747 100644 --- a/app/src/main/res/layout/layout_toolbar.xml +++ b/app/src/main/res/layout/layout_toolbar.xml @@ -27,7 +27,7 @@ </android.support.v7.widget.Toolbar> <de.kuschku.quasseldroid_ng.util.ui.MaterialContentLoadingProgressBar - android:id="@+id/progressBar" + android:id="@+id/progress_bar" style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/app/src/main/res/menu/editor.xml b/app/src/main/res/menu/editor.xml new file mode 100644 index 000000000..f50026af7 --- /dev/null +++ b/app/src/main/res/menu/editor.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/format_bold" + android:icon="@drawable/ic_format_bold" + android:title="@string/label_bold" + app:showAsAction="always" /> + <item + android:id="@+id/format_italic" + android:icon="@drawable/ic_format_italic" + android:title="@string/label_italic" + app:showAsAction="always" /> + <item + android:id="@+id/format_underline" + android:icon="@drawable/ic_format_underline" + android:title="@string/label_underline" + app:showAsAction="always" /> + <item + android:id="@+id/format_strikethrough" + android:icon="@drawable/ic_format_strikethrough" + android:title="@string/label_strikethrough" + app:showAsAction="always" /> + <item + android:id="@+id/format_monospace" + android:icon="@drawable/ic_format_monospace" + android:title="@string/label_monospace" + app:showAsAction="always" /> + <!-- + <item + android:id="@+id/format_foreground" + android:icon="@drawable/ic_format_foreground" + android:title="@string/label_foreground" + app:showAsAction="always" /> + <item + android:id="@+id/format_background" + android:icon="@drawable/ic_format_background" + android:title="@string/label_background" + app:showAsAction="always" /> + --> + <item + android:id="@+id/format_clear" + android:icon="@drawable/ic_format_clear" + android:title="@string/label_clear_formatting" + app:showAsAction="always" /> +</menu> \ No newline at end of file diff --git a/app/src/main/res/menu/input_panel.xml b/app/src/main/res/menu/input_panel.xml new file mode 100644 index 000000000..af252a356 --- /dev/null +++ b/app/src/main/res/menu/input_panel.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/input_history" + android:icon="@drawable/ic_history" + android:title="@string/label_input_history" + app:showAsAction="always" /> +</menu> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c20443db3..1f0949e22 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,4 +40,12 @@ <string name="notification_channel_highlight_title">Highlight</string> <string name="buffer_delete_confirmation">Do you want to delete this buffer permanently?</string> + <string name="label_bold">Bold</string> + <string name="label_italic">Italic</string> + <string name="label_strikethrough">Strikethrough</string> + <string name="label_underline">Underline</string> + <string name="label_monospace">Monospace</string> + <string name="label_foreground">Foreground</string> + <string name="label_background">Background</string> + <string name="label_clear_formatting">Clear Formatting</string> </resources> diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt index ae2286048..582a41b8b 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt @@ -10,7 +10,7 @@ import java.nio.charset.CharsetDecoder import java.nio.charset.CharsetEncoder abstract class StringSerializer( - private val encoder: CharsetEncoder, + private var encoder: CharsetEncoder, private val decoder: CharsetDecoder, private val trailingNullBytes: Int ) : Serializer<String?> { @@ -51,6 +51,7 @@ abstract class StringSerializer( val charBuffer = charBuffer(data.length) charBuffer.put(data) charBuffer.flip() + encoder = encoder.charset().newEncoder() val byteBuffer = encoder.encode(charBuffer) IntSerializer.serialize(buffer, byteBuffer.remaining() + trailingNullBytes, features) buffer.put(byteBuffer) @@ -70,6 +71,7 @@ abstract class StringSerializer( val charBuffer = charBuffer(data.length) charBuffer.put(data) charBuffer.flip() + encoder = encoder.charset().newEncoder() return encoder.encode(charBuffer) } } catch (e: Throwable) { -- GitLab