From 6ec2a1293a58e601349e39c625d6f107c631a3a2 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Fri, 30 Mar 2018 14:00:12 +0200 Subject: [PATCH] Implement color chooser --- app/build.gradle.kts | 6 +- .../quasseldroid/ui/chat/ChatActivity.kt | 15 +- .../quasseldroid/ui/chat/input/Editor.kt | 79 +- .../ui/chat/input/FormatHandler.kt | 130 ++- .../ui/chat/messages/MessageListFragment.kt | 33 +- .../util/irc/format/IrcFormatDeserializer.kt | 8 +- .../format/spans/IrcBackgroundColorSpan.kt | 16 +- .../format/spans/IrcForegroundColorSpan.kt | 16 +- .../format/spans/IrcHexBackgroundColorSpan.kt | 9 - .../format/spans/IrcHexForegroundColorSpan.kt | 9 - .../util/ui/ColorChooserDialog.java | 798 ++++++++++++++++++ app/src/main/res/drawable/bg_transparent.xml | 5 + .../main/res/layout/dialog_colorchooser.xml | 19 + .../res/layout/dialog_colorchooser_custom.xml | 236 ++++++ .../layout/dialog_colorchooser_presets.xml | 14 + app/src/main/res/layout/layout_history.xml | 94 ++- app/src/main/res/layout/widget_formatting.xml | 4 +- app/src/main/res/values-de/strings.xml | 6 +- app/src/main/res/values-large/dimens.xml | 4 + app/src/main/res/values/dimens.xml | 2 + app/src/main/res/values/strings.xml | 6 +- app/src/main/res/values/themes_base.xml | 246 +++--- 22 files changed, 1535 insertions(+), 220 deletions(-) delete mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexBackgroundColorSpan.kt delete mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexForegroundColorSpan.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java create mode 100644 app/src/main/res/drawable/bg_transparent.xml create mode 100644 app/src/main/res/layout/dialog_colorchooser.xml create mode 100644 app/src/main/res/layout/dialog_colorchooser_custom.xml create mode 100644 app/src/main/res/layout/dialog_colorchooser_presets.xml create mode 100644 app/src/main/res/values-large/dimens.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fc09650aa..a0d68a412 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -142,8 +142,12 @@ dependencies { // UI implementation("me.zhanghai.android.materialprogressbar", "library", "1.4.2") - implementation("com.afollestad.material-dialogs", "core", "0.9.6.0") + withVersion("0.9.6.0") { + implementation("com.afollestad.material-dialogs", "core", version) + implementation("com.afollestad.material-dialogs", "commons", version) + } implementation("me.saket", "better-link-movement-method", "2.1.0") + implementation("com.nex3z", "flow-layout", "1.2.2") implementation(project(":slidingpanel")) // Quality Assurance diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt index 42b7c8edd..6a74f3d74 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt @@ -178,6 +178,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc .title(R.string.label_error_init) .content(Html.fromHtml(it.errorString)) .neutralText(R.string.label_close) + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) .build() .show() is HandshakeMessage.CoreSetupReject -> @@ -185,6 +188,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc .title(R.string.label_error_setup) .content(Html.fromHtml(it.errorString)) .neutralText(R.string.label_close) + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) .build() .show() is HandshakeMessage.ClientLoginReject -> @@ -220,6 +226,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc backend.value.orNull()?.updateUserDataAndLogin(user, pass) } } + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) .build() dialog.customView?.run { val userField = findViewById<EditText>(R.id.user) @@ -233,6 +242,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } } + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) .build() .show() } @@ -355,7 +367,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc .items(R.array.message_filter_types) .itemsIds(flags) .itemsCallbackMultiChoice(selectedIndices, { _, _, _ -> false }) - .positiveText(R.string.label_select_multiple) + .positiveText(R.string.label_select) .negativeText(R.string.label_cancel) .onPositive { dialog, _ -> val selected = dialog.selectedIndices ?: emptyArray() @@ -371,6 +383,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc }.negativeColorAttr(R.attr.colorTextPrimary) .backgroundColorAttr(R.attr.colorBackgroundCard) .contentColorAttr(R.attr.colorTextPrimary) + .titleColorAttr(R.attr.colorTextPrimary) .build() .show() } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/Editor.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/Editor.kt index 4ecee7ba8..b5b2d0750 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/Editor.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/Editor.kt @@ -2,6 +2,9 @@ package de.kuschku.quasseldroid.ui.chat.input import android.arch.lifecycle.LiveData import android.arch.lifecycle.Observer +import android.support.annotation.ColorInt +import android.support.annotation.StringRes +import android.support.v4.app.FragmentActivity import android.support.v7.app.AppCompatActivity import android.support.v7.widget.* import android.text.Editable @@ -16,6 +19,7 @@ import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.util.helper.* +import de.kuschku.quasseldroid.util.ui.ColorChooserDialog import de.kuschku.quasseldroid.util.ui.EditTextSelectionChange import de.kuschku.quasseldroid.viewmodel.data.AutoCompleteItem import io.reactivex.Observable @@ -104,9 +108,15 @@ class Editor( @BindView(R.id.action_format_foreground) lateinit var foregroundButton: View + @BindView(R.id.action_format_foreground_preview) + lateinit var foregroundButtonPreview: View + @BindView(R.id.action_format_background) lateinit var backgroundButton: View + @BindView(R.id.action_format_background_preview) + lateinit var backgroundButtonPreview: View + @BindView(R.id.action_format_clear) lateinit var clearButton: View @@ -225,12 +235,30 @@ class Editor( TooltipCompat.setTooltipText(monospaceButton, monospaceButton.contentDescription) foregroundButton.setOnClickListener { - + showColorChooser( + activity, + R.string.label_foreground, + formatHandler.foregroundColor(chatline.selection) + ?: formatHandler.defaultForegroundColor + ) { color -> + formatHandler.toggleForeground(chatline.selection, color, + formatHandler.mircColorMap[color]) + updateButtons(chatline.selection) + } } TooltipCompat.setTooltipText(foregroundButton, foregroundButton.contentDescription) backgroundButton.setOnClickListener { - + showColorChooser( + activity, + R.string.label_background, + formatHandler.backgroundColor(chatline.selection) + ?: formatHandler.defaultBackgroundColor + ) { color -> + formatHandler.toggleBackground(chatline.selection, color, + formatHandler.mircColorMap[color]) + updateButtons(chatline.selection) + } } TooltipCompat.setTooltipText(backgroundButton, backgroundButton.contentDescription) @@ -262,12 +290,59 @@ class Editor( } } + private fun showColorChooser( + activity: FragmentActivity, @StringRes title: Int, @ColorInt preselect: Int, f: (Int?) -> Unit + ) { + var selectedColor: Int? = null + ColorChooserDialog.Builder(chatline.context, title) + .customColors(intArrayOf( + formatHandler.mircColors[0], + formatHandler.mircColors[1], + formatHandler.mircColors[2], + formatHandler.mircColors[3], + formatHandler.mircColors[4], + formatHandler.mircColors[5], + formatHandler.mircColors[6], + formatHandler.mircColors[7], + formatHandler.mircColors[8], + formatHandler.mircColors[9], + formatHandler.mircColors[10], + formatHandler.mircColors[11], + formatHandler.mircColors[12], + formatHandler.mircColors[13], + formatHandler.mircColors[14], + formatHandler.mircColors[15] + ), null) + .doneButton(R.string.label_select) + .cancelButton(R.string.label_reset) + .backButton(R.string.label_back) + .customButton(R.string.label_colors_custom) + .presetsButton(R.string.label_colors_mirc) + .preselect(preselect) + .dynamicButtonColor(false) + .allowUserColorInputAlpha(false) + .callback(object : ColorChooserDialog.ColorCallback { + override fun onColorSelection(dialog: ColorChooserDialog, color: Int) { + selectedColor = color + } + + override fun onColorChooserDismissed(dialog: ColorChooserDialog) { + f(selectedColor) + } + }) + .show(activity) + } + fun updateButtons(selection: IntRange) { boldButton.isSelected = formatHandler.isBold(selection) italicButton.isSelected = formatHandler.isItalic(selection) underlineButton.isSelected = formatHandler.isUnderline(selection) strikethroughButton.isSelected = formatHandler.isStrikethrough(selection) monospaceButton.isSelected = formatHandler.isMonospace(selection) + foregroundButtonPreview.setBackgroundColor(formatHandler.foregroundColor(selection) + ?: formatHandler.defaultForegroundColor) + backgroundButtonPreview.setBackgroundColor(formatHandler.backgroundColor(selection) + ?: formatHandler.defaultBackgroundColor) } fun onStart() { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/FormatHandler.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/FormatHandler.kt index f7c0d8547..af1987c58 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/FormatHandler.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/FormatHandler.kt @@ -1,25 +1,60 @@ package de.kuschku.quasseldroid.ui.chat.input import android.graphics.Typeface +import android.support.annotation.ColorInt import android.text.Editable import android.text.SpannableString 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.text.style.* import android.widget.EditText +import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.ui.chat.ChatActivity -import de.kuschku.quasseldroid.util.helper.lastWordIndices -import de.kuschku.quasseldroid.util.helper.lineSequence -import de.kuschku.quasseldroid.util.helper.selection -import de.kuschku.quasseldroid.util.helper.without +import de.kuschku.quasseldroid.util.helper.* import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer import de.kuschku.quasseldroid.util.irc.format.spans.* class FormatHandler( private val editText: EditText ) { + val mircColors = editText.context.theme.styledAttributes( + R.attr.mircColor00, R.attr.mircColor01, R.attr.mircColor02, R.attr.mircColor03, + R.attr.mircColor04, R.attr.mircColor05, R.attr.mircColor06, R.attr.mircColor07, + R.attr.mircColor08, R.attr.mircColor09, R.attr.mircColor10, R.attr.mircColor11, + R.attr.mircColor12, R.attr.mircColor13, R.attr.mircColor14, R.attr.mircColor15, + R.attr.mircColor16, R.attr.mircColor17, R.attr.mircColor18, R.attr.mircColor19, + R.attr.mircColor20, R.attr.mircColor21, R.attr.mircColor22, R.attr.mircColor23, + R.attr.mircColor24, R.attr.mircColor25, R.attr.mircColor26, R.attr.mircColor27, + R.attr.mircColor28, R.attr.mircColor29, R.attr.mircColor30, R.attr.mircColor31, + R.attr.mircColor32, R.attr.mircColor33, R.attr.mircColor34, R.attr.mircColor35, + R.attr.mircColor36, R.attr.mircColor37, R.attr.mircColor38, R.attr.mircColor39, + R.attr.mircColor40, R.attr.mircColor41, R.attr.mircColor42, R.attr.mircColor43, + R.attr.mircColor44, R.attr.mircColor45, R.attr.mircColor46, R.attr.mircColor47, + R.attr.mircColor48, R.attr.mircColor49, R.attr.mircColor50, R.attr.mircColor51, + R.attr.mircColor52, R.attr.mircColor53, R.attr.mircColor54, R.attr.mircColor55, + R.attr.mircColor56, R.attr.mircColor57, R.attr.mircColor58, R.attr.mircColor59, + R.attr.mircColor60, R.attr.mircColor61, R.attr.mircColor62, R.attr.mircColor63, + R.attr.mircColor64, R.attr.mircColor65, R.attr.mircColor66, R.attr.mircColor67, + R.attr.mircColor68, R.attr.mircColor69, R.attr.mircColor70, R.attr.mircColor71, + R.attr.mircColor72, R.attr.mircColor73, R.attr.mircColor74, R.attr.mircColor75, + R.attr.mircColor76, R.attr.mircColor77, R.attr.mircColor78, R.attr.mircColor79, + R.attr.mircColor80, R.attr.mircColor81, R.attr.mircColor82, R.attr.mircColor83, + R.attr.mircColor84, R.attr.mircColor85, R.attr.mircColor86, R.attr.mircColor87, + R.attr.mircColor88, R.attr.mircColor89, R.attr.mircColor90, R.attr.mircColor91, + R.attr.mircColor92, R.attr.mircColor93, R.attr.mircColor94, R.attr.mircColor95, + R.attr.mircColor96, R.attr.mircColor97, R.attr.mircColor98 + ) { + (0..98).map { getColor(it, 0) } + } + val mircColorMap = mircColors.withIndex().map { (key, value) -> key to value }.toMap() + + val defaultForegroundColor = editText.context.theme.styledAttributes(R.attr.colorForeground) { + getColor(0, 0) + } + + val defaultBackgroundColor = editText.context.theme.styledAttributes(R.attr.colorBackground) { + getColor(0, 0) + } + private val serializer = IrcFormatSerializer(editText.context) val formattedText: Sequence<String> get() = editText.text.lineSequence().map { serializer.toEscapeCodes(SpannableString(it)) } @@ -144,14 +179,93 @@ class FormatHandler( } } + fun foregroundColors(range: IntRange) = editText.text.spans<ForegroundColorSpan>(range) + fun foregroundColor(range: IntRange) = foregroundColors(range).singleOrNull()?.foregroundColor + fun toggleForeground(range: IntRange, @ColorInt color: Int? = null, mircColor: Int? = null) { + editText.text.removeSpans<ForegroundColorSpan, IrcForegroundColorSpan<*>>(range) { span -> + val mirc = mircColorMap[span.foregroundColor] + when { + span is IrcForegroundColorSpan<*> -> span + mirc != null -> IrcForegroundColorSpan.MIRC(mirc, span.foregroundColor) + else -> IrcForegroundColorSpan.HEX(span.foregroundColor) + } + } + + if (color != null) { + if (mircColor != null) { + editText.text.setSpan( + IrcForegroundColorSpan.MIRC(mircColor, color), + range.start, + range.last + 1, + Spanned.SPAN_INCLUSIVE_INCLUSIVE + ) + } else { + editText.text.setSpan( + IrcForegroundColorSpan.HEX(color), + range.start, + range.last + 1, + Spanned.SPAN_INCLUSIVE_INCLUSIVE + ) + } + } + } + + fun backgroundColors(range: IntRange) = editText.text.spans<BackgroundColorSpan>(range) + fun backgroundColor(range: IntRange) = backgroundColors(range).singleOrNull()?.backgroundColor + fun toggleBackground(range: IntRange, @ColorInt color: Int? = null, mircColor: Int? = null) { + editText.text.removeSpans<BackgroundColorSpan, IrcBackgroundColorSpan<*>>(range) { span -> + val mirc = mircColorMap[span.backgroundColor] + when { + span is IrcBackgroundColorSpan<*> -> span + mirc != null -> IrcBackgroundColorSpan.MIRC(mirc, span.backgroundColor) + else -> IrcBackgroundColorSpan.HEX(span.backgroundColor) + } + } + + if (color != null) { + if (mircColor != null) { + editText.text.setSpan( + IrcBackgroundColorSpan.MIRC(mircColor, color), + range.start, + range.last + 1, + Spanned.SPAN_INCLUSIVE_INCLUSIVE + ) + } else { + editText.text.setSpan( + IrcBackgroundColorSpan.HEX(color), + range.start, + range.last + 1, + Spanned.SPAN_INCLUSIVE_INCLUSIVE + ) + } + } + } + fun clearFormatting(range: IntRange) { toggleBold(range, false) toggleItalic(range, false) toggleUnderline(range, false) toggleStrikethrough(range, false) toggleMonospace(range, false) + toggleForeground(range, null, null) + toggleBackground(range, null, null) } + private inline fun <reified U> Spanned.spans(range: IntRange) = + getSpans(range.start, range.endInclusive + 1, U::class.java).filter { + getSpanFlags(it) and Spanned.SPAN_COMPOSING == 0 && + (getSpanEnd(it) != range.start || + getSpanFlags(it) and 0x02 != 0) + } + + private inline fun <reified U> Spanned.spans(range: IntRange, f: (U) -> Boolean) = + getSpans(range.start, range.last + 1, U::class.java).filter { + f(it) && + getSpanFlags(it) and Spanned.SPAN_COMPOSING == 0 && + (getSpanEnd(it) != range.start || + getSpanFlags(it) and 0x02 != 0) + } + private inline fun <reified U> Spanned.hasSpans(range: IntRange) = getSpans(range.start, range.endInclusive + 1, U::class.java).any { getSpanFlags(it) and Spanned.SPAN_COMPOSING == 0 && diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt index 7e0ef6577..41cfe09b6 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt @@ -11,6 +11,7 @@ import android.support.design.widget.FloatingActionButton import android.support.v4.widget.SwipeRefreshLayout import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView +import android.text.SpannableStringBuilder import android.view.* import butterknife.BindView import butterknife.ButterKnife @@ -63,36 +64,36 @@ class MessageListFragment : ServiceBoundFragment() { private val actionModeCallback = object : ActionMode.Callback { override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?) = when (item?.itemId) { R.id.action_copy -> { - val data = viewModel.selectedMessages.value.values.sortedBy { + val builder = SpannableStringBuilder() + viewModel.selectedMessages.value.values.sortedBy { it.id - }.joinToString("\n") { - SpanFormatter.format( - getString(R.string.message_format_copy), - it.time, - it.content - ) + }.map { + SpanFormatter.format(getString(R.string.message_format_copy), it.time, it.content) + }.forEach { + builder.append(it) + builder.append("\n") } val clipboard = requireActivity().systemService<ClipboardManager>() - val clip = ClipData.newPlainText(null, data) + val clip = ClipData.newPlainText(null, builder) clipboard.primaryClip = clip actionMode?.finish() true } R.id.action_share -> { - val data = viewModel.selectedMessages.value.values.sortedBy { + val builder = SpannableStringBuilder() + viewModel.selectedMessages.value.values.sortedBy { it.id - }.joinToString("\n") { - SpanFormatter.format( - getString(R.string.message_format_copy), - it.time, - it.content - ) + }.map { + SpanFormatter.format(getString(R.string.message_format_copy), it.time, it.content) + }.forEach { + builder.append(it) + builder.append("\n") } val intent = Intent(Intent.ACTION_SEND) intent.type = "text/plain" - intent.putExtra(Intent.EXTRA_TEXT, data) + intent.putExtra(Intent.EXTRA_TEXT, builder) requireContext().startActivity( Intent.createChooser( intent, diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt index 8b36cb6c8..ea59e13db 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt @@ -350,13 +350,13 @@ class IrcFormatDeserializer @Inject constructor() { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { if (foreground >= 0) { editable.setSpan( - IrcHexForegroundColorSpan(foreground or 0xFFFFFF.inv()), from, to, + IrcForegroundColorSpan.HEX(foreground or 0xFFFFFF.inv()), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) } if (background >= 0) { editable.setSpan( - IrcHexBackgroundColorSpan(background or 0xFFFFFF.inv()), from, to, + IrcBackgroundColorSpan.HEX(background or 0xFFFFFF.inv()), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) } @@ -369,13 +369,13 @@ class IrcFormatDeserializer @Inject constructor() { override fun applyTo(editable: SpannableStringBuilder, from: Int, to: Int) { if (foreground.toInt() >= 0 && foreground.toInt() < mircColors.size) { editable.setSpan( - IrcForegroundColorSpan(foreground.toInt(), mircColors[foreground.toInt()]), from, to, + IrcForegroundColorSpan.MIRC(foreground.toInt(), mircColors[foreground.toInt()]), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) } if (background.toInt() >= 0 && background.toInt() < mircColors.size) { editable.setSpan( - IrcBackgroundColorSpan(background.toInt(), mircColors[background.toInt()]), from, to, + IrcBackgroundColorSpan.MIRC(background.toInt(), mircColors[background.toInt()]), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcBackgroundColorSpan.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcBackgroundColorSpan.kt index 934efb741..8a4562753 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcBackgroundColorSpan.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcBackgroundColorSpan.kt @@ -3,9 +3,15 @@ package de.kuschku.quasseldroid.util.irc.format.spans import android.support.annotation.ColorInt import android.text.style.BackgroundColorSpan -class IrcBackgroundColorSpan( - val mircColor: Int, - @ColorInt color: Int -) : BackgroundColorSpan(color), Copyable<IrcBackgroundColorSpan> { - override fun copy() = IrcBackgroundColorSpan(mircColor, backgroundColor) +sealed class IrcBackgroundColorSpan<T : IrcBackgroundColorSpan<T>>(@ColorInt color: Int) : + BackgroundColorSpan(color), Copyable<T> { + class MIRC(val mircColor: Int, @ColorInt color: Int) : + IrcBackgroundColorSpan<MIRC>(color), Copyable<MIRC> { + override fun copy() = MIRC(mircColor, backgroundColor) + } + + class HEX(@ColorInt color: Int) : + IrcBackgroundColorSpan<HEX>(color), Copyable<HEX> { + override fun copy() = HEX(backgroundColor) + } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcForegroundColorSpan.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcForegroundColorSpan.kt index 12641c5c8..36acd5aae 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcForegroundColorSpan.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcForegroundColorSpan.kt @@ -3,9 +3,15 @@ package de.kuschku.quasseldroid.util.irc.format.spans import android.support.annotation.ColorInt import android.text.style.ForegroundColorSpan -class IrcForegroundColorSpan( - val mircColor: Int, - @ColorInt color: Int -) : ForegroundColorSpan(color), Copyable<IrcForegroundColorSpan> { - override fun copy() = IrcForegroundColorSpan(mircColor, foregroundColor) +sealed class IrcForegroundColorSpan<T : IrcForegroundColorSpan<T>>(@ColorInt color: Int) : + ForegroundColorSpan(color), Copyable<T> { + class MIRC(val mircColor: Int, @ColorInt color: Int) : + IrcForegroundColorSpan<MIRC>(color), Copyable<MIRC> { + override fun copy() = MIRC(mircColor, foregroundColor) + } + + class HEX(@ColorInt color: Int) : + IrcForegroundColorSpan<HEX>(color), Copyable<HEX> { + override fun copy() = HEX(foregroundColor) + } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexBackgroundColorSpan.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexBackgroundColorSpan.kt deleted file mode 100644 index 2269de782..000000000 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexBackgroundColorSpan.kt +++ /dev/null @@ -1,9 +0,0 @@ -package de.kuschku.quasseldroid.util.irc.format.spans - -import android.support.annotation.ColorInt -import android.text.style.BackgroundColorSpan - -class IrcHexBackgroundColorSpan(@ColorInt color: Int) : BackgroundColorSpan(color), - Copyable<IrcHexBackgroundColorSpan> { - override fun copy() = IrcHexBackgroundColorSpan(backgroundColor) -} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexForegroundColorSpan.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexForegroundColorSpan.kt deleted file mode 100644 index f3d680b1c..000000000 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/IrcHexForegroundColorSpan.kt +++ /dev/null @@ -1,9 +0,0 @@ -package de.kuschku.quasseldroid.util.irc.format.spans - -import android.support.annotation.ColorInt -import android.text.style.ForegroundColorSpan - -class IrcHexForegroundColorSpan(@ColorInt color: Int) : ForegroundColorSpan(color), - Copyable<IrcHexForegroundColorSpan> { - override fun copy() = IrcHexForegroundColorSpan(foregroundColor) -} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java b/app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java new file mode 100644 index 000000000..811ff4d82 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/ColorChooserDialog.java @@ -0,0 +1,798 @@ +package de.kuschku.quasseldroid.util.ui; + +import android.annotation.SuppressLint; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.ArrayRes; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringDef; +import android.support.annotation.StringRes; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.content.res.ResourcesCompat; +import android.text.Editable; +import android.text.InputFilter; +import android.text.TextWatcher; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.GridView; +import android.widget.SeekBar; +import android.widget.TextView; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.afollestad.materialdialogs.Theme; +import com.afollestad.materialdialogs.color.CircleView; +import com.afollestad.materialdialogs.internal.MDTintHelper; +import com.afollestad.materialdialogs.util.DialogUtils; + +import java.io.Serializable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Locale; + +import de.kuschku.quasseldroid.R; + +/** + * @author Aidan Follestad (afollestad) + */ +@SuppressWarnings({"FieldCanBeLocal", "ConstantConditions"}) +public class ColorChooserDialog extends DialogFragment + implements View.OnClickListener, View.OnLongClickListener { + + public static final String TAG_PRIMARY = "[MD_COLOR_CHOOSER]"; + public static final String TAG_ACCENT = "[MD_COLOR_CHOOSER]"; + public static final String TAG_CUSTOM = "[MD_COLOR_CHOOSER]"; + private int[] colorsTop; + @Nullable + private int[][] colorsSub; + private int circleSize; + private GridView grid; + private View colorChooserCustomFrame; + private EditText customColorHex; + private View customColorIndicator; + private TextWatcher customColorTextWatcher; + private SeekBar customSeekA; + private TextView customSeekAValue; + private SeekBar customSeekR; + private TextView customSeekRValue; + private SeekBar customSeekG; + private TextView customSeekGValue; + private SeekBar customSeekB; + private TextView customSeekBValue; + private SeekBar.OnSeekBarChangeListener customColorRgbListener; + private int selectedCustomColor; + + public ColorChooserDialog() { + } + + @Nullable + public static ColorChooserDialog findVisible( + @NonNull FragmentManager fragmentManager, @ColorChooserTag String tag) { + Fragment frag = fragmentManager.findFragmentByTag(tag); + if (frag != null && frag instanceof ColorChooserDialog) { + return (ColorChooserDialog) frag; + } + return null; + } + + private void generateColors() { + Builder builder = getBuilder(); + colorsTop = builder.colorsTop; + colorsSub = builder.colorsSub; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("top_index", topIndex()); + outState.putBoolean("in_sub", isInSub()); + outState.putInt("sub_index", subIndex()); + outState.putBoolean( + "in_custom", + colorChooserCustomFrame != null && colorChooserCustomFrame.getVisibility() == View.VISIBLE); + } + + private boolean isInSub() { + return getArguments().getBoolean("in_sub", false); + } + + private void isInSub(boolean value) { + getArguments().putBoolean("in_sub", value); + } + + private int topIndex() { + return getArguments().getInt("top_index", -1); + } + + private void topIndex(int value) { + if (value > -1) { + findSubIndexForColor(value, colorsTop[value]); + } + getArguments().putInt("top_index", value); + } + + private int subIndex() { + if (colorsSub == null) { + return -1; + } + return getArguments().getInt("sub_index", -1); + } + + private void subIndex(int value) { + if (colorsSub == null) { + return; + } + getArguments().putInt("sub_index", value); + } + + @StringRes + public int getTitle() { + Builder builder = getBuilder(); + int title; + if (isInSub()) { + title = builder.titleSub; + } else { + title = builder.title; + } + if (title == 0) { + title = builder.title; + } + return title; + } + + public String tag() { + Builder builder = getBuilder(); + if (builder.tag != null) { + return builder.tag; + } else { + return super.getTag(); + } + } + + @Override + public void onClick(View v) { + if (v.getTag() != null) { + final String[] tag = ((String) v.getTag()).split(":"); + final int index = Integer.parseInt(tag[0]); + final MaterialDialog dialog = (MaterialDialog) getDialog(); + final Builder builder = getBuilder(); + + if (isInSub()) { + subIndex(index); + } else { + topIndex(index); + if (colorsSub != null && index < colorsSub.length) { + dialog.setActionButton(DialogAction.NEGATIVE, builder.backBtn); + isInSub(true); + } + } + + if (builder.allowUserCustom) { + selectedCustomColor = getSelectedColor(); + } + invalidateDynamicButtonColors(); + invalidate(); + } + } + + @Override + public boolean onLongClick(View v) { + if (v.getTag() != null) { + final String[] tag = ((String) v.getTag()).split(":"); + final int color = Integer.parseInt(tag[1]); + ((CircleView) v).showHint(color); + return true; + } + return false; + } + + private void invalidateDynamicButtonColors() { + final MaterialDialog dialog = (MaterialDialog) getDialog(); + if (dialog == null) { + return; + } + final Builder builder = getBuilder(); + if (builder.dynamicButtonColor) { + int selectedColor = getSelectedColor(); + if (Color.alpha(selectedColor) < 64 + || (Color.red(selectedColor) > 247 + && Color.green(selectedColor) > 247 + && Color.blue(selectedColor) > 247)) { + // Once we get close to white or transparent, + // the action buttons and seekbars will be a very light gray. + selectedColor = Color.parseColor("#DEDEDE"); + } + + if (getBuilder().dynamicButtonColor) { + dialog.getActionButton(DialogAction.POSITIVE).setTextColor(selectedColor); + dialog.getActionButton(DialogAction.NEGATIVE).setTextColor(selectedColor); + dialog.getActionButton(DialogAction.NEUTRAL).setTextColor(selectedColor); + } + + if (customSeekR != null) { + if (customSeekA.getVisibility() == View.VISIBLE) { + MDTintHelper.setTint(customSeekA, selectedColor); + } + MDTintHelper.setTint(customSeekR, selectedColor); + MDTintHelper.setTint(customSeekG, selectedColor); + MDTintHelper.setTint(customSeekB, selectedColor); + } + } + } + + @ColorInt + private int getSelectedColor() { + if (colorChooserCustomFrame != null + && colorChooserCustomFrame.getVisibility() == View.VISIBLE) { + return selectedCustomColor; + } + + int color = 0; + if (subIndex() > -1) { + color = colorsSub[topIndex()][subIndex()]; + } else if (topIndex() > -1) { + color = colorsTop[topIndex()]; + } + return color; + } + + private void findSubIndexForColor(int topIndex, int color) { + if (colorsSub == null || colorsSub.length - 1 < topIndex) { + return; + } + int[] subColors = colorsSub[topIndex]; + for (int subIndex = 0; subIndex < subColors.length; subIndex++) { + if (subColors[subIndex] == color) { + subIndex(subIndex); + break; + } + } + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + if (getArguments() == null || !getArguments().containsKey("builder")) { + throw new IllegalStateException( + "ColorChooserDialog should be created using its Builder interface."); + } + generateColors(); + + int preselectColor; + boolean foundPreselectColor = false; + + if (savedInstanceState != null) { + preselectColor = getSelectedColor(); + } else { + if (getBuilder().setPreselectionColor) { + preselectColor = getBuilder().preselectColor; + if (preselectColor != 0) { + for (int topIndex = 0; topIndex < colorsTop.length; topIndex++) { + if (colorsTop[topIndex] == preselectColor) { + topIndex(topIndex); + if (colorsSub != null) { + findSubIndexForColor(topIndex, preselectColor); + } else { + subIndex(5); + } + break; + } + + if (colorsSub != null) { + for (int subIndex = 0; subIndex < colorsSub[topIndex].length; subIndex++) { + if (colorsSub[topIndex][subIndex] == preselectColor) { + foundPreselectColor = true; + topIndex(topIndex); + subIndex(subIndex); + break; + } + } + if (foundPreselectColor) { + break; + } + } + } + } + } else { + preselectColor = Color.BLACK; + } + } + + circleSize = getResources().getDimensionPixelSize(R.dimen.colorchooser_circlesize); + final Builder builder = getBuilder(); + + MaterialDialog.Builder bd = + new MaterialDialog.Builder(getActivity()) + .title(getTitle()) + .autoDismiss(false) + .customView(R.layout.dialog_colorchooser, false) + .negativeText(builder.cancelBtn) + .positiveText(builder.doneBtn) + .neutralText(builder.allowUserCustom ? builder.customBtn : 0) + .typeface(builder.mediumFont, builder.regularFont) + .titleColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) + .onPositive( + new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + builder.callback.onColorSelection(ColorChooserDialog.this, getSelectedColor()); + dismiss(); + } + }) + .onNegative( + new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + if (isInSub()) { + dialog.setActionButton(DialogAction.NEGATIVE, getBuilder().cancelBtn); + isInSub(false); + subIndex(-1); // Do this to avoid ArrayIndexOutOfBoundsException + invalidate(); + } else { + dialog.cancel(); + } + } + }) + .onNeutral( + new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + toggleCustom(dialog); + } + }) + .showListener( + new DialogInterface.OnShowListener() { + @Override + public void onShow(DialogInterface dialog) { + invalidateDynamicButtonColors(); + } + }); + + if (builder.theme != null) { + bd.theme(builder.theme); + } + + final MaterialDialog dialog = bd.build(); + final View v = dialog.getCustomView(); + grid = v.findViewById(R.id.md_grid); + + if (builder.allowUserCustom) { + selectedCustomColor = preselectColor; + colorChooserCustomFrame = v.findViewById(R.id.md_colorChooserCustomFrame); + customColorHex = v.findViewById(R.id.md_hexInput); + customColorIndicator = v.findViewById(R.id.md_colorIndicator); + customSeekA = v.findViewById(R.id.md_colorA); + customSeekAValue = v.findViewById(R.id.md_colorAValue); + customSeekR = v.findViewById(R.id.md_colorR); + customSeekRValue = v.findViewById(R.id.md_colorRValue); + customSeekG = v.findViewById(R.id.md_colorG); + customSeekGValue = v.findViewById(R.id.md_colorGValue); + customSeekB = v.findViewById(R.id.md_colorB); + customSeekBValue = v.findViewById(R.id.md_colorBValue); + + if (!builder.allowUserCustomAlpha) { + v.findViewById(R.id.md_colorALabel).setVisibility(View.GONE); + customSeekA.setVisibility(View.GONE); + customSeekAValue.setVisibility(View.GONE); + customColorHex.setHint("2196F3"); + customColorHex.setFilters(new InputFilter[]{new InputFilter.LengthFilter(6)}); + } else { + customColorHex.setHint("FF2196F3"); + customColorHex.setFilters(new InputFilter[]{new InputFilter.LengthFilter(8)}); + } + } + + invalidate(); + return dialog; + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if (getBuilder().callback != null) { + getBuilder().callback.onColorChooserDismissed(this); + } + } + + private void toggleCustom(MaterialDialog dialog) { + if (dialog == null) { + dialog = (MaterialDialog) getDialog(); + } + if (grid.getVisibility() == View.VISIBLE) { + dialog.setTitle(getBuilder().customBtn); + dialog.setActionButton(DialogAction.NEUTRAL, getBuilder().presetsBtn); + dialog.setActionButton(DialogAction.NEGATIVE, getBuilder().cancelBtn); + grid.setVisibility(View.INVISIBLE); + colorChooserCustomFrame.setVisibility(View.VISIBLE); + + customColorTextWatcher = + new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + try { + selectedCustomColor = Color.parseColor("#" + s.toString()); + } catch (IllegalArgumentException e) { + selectedCustomColor = Color.BLACK; + } + customColorIndicator.setBackgroundColor(selectedCustomColor); + if (customSeekA.getVisibility() == View.VISIBLE) { + int alpha = Color.alpha(selectedCustomColor); + customSeekA.setProgress(alpha); + customSeekAValue.setText(String.format(Locale.US, "%d", alpha)); + } + int red = Color.red(selectedCustomColor); + customSeekR.setProgress(red); + int green = Color.green(selectedCustomColor); + customSeekG.setProgress(green); + int blue = Color.blue(selectedCustomColor); + customSeekB.setProgress(blue); + isInSub(false); + topIndex(-1); + subIndex(-1); + invalidateDynamicButtonColors(); + } + + @Override + public void afterTextChanged(Editable s) { + } + }; + + customColorHex.addTextChangedListener(customColorTextWatcher); + customColorRgbListener = + new SeekBar.OnSeekBarChangeListener() { + + @SuppressLint("DefaultLocale") + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + if (getBuilder().allowUserCustomAlpha) { + int color = + Color.argb( + customSeekA.getProgress(), + customSeekR.getProgress(), + customSeekG.getProgress(), + customSeekB.getProgress()); + customColorHex.setText(String.format("%08X", color)); + } else { + int color = + Color.rgb( + customSeekR.getProgress(), + customSeekG.getProgress(), + customSeekB.getProgress()); + customColorHex.setText(String.format("%06X", 0xFFFFFF & color)); + } + } + customSeekAValue.setText(String.format("%d", customSeekA.getProgress())); + customSeekRValue.setText(String.format("%d", customSeekR.getProgress())); + customSeekGValue.setText(String.format("%d", customSeekG.getProgress())); + customSeekBValue.setText(String.format("%d", customSeekB.getProgress())); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }; + + customSeekR.setOnSeekBarChangeListener(customColorRgbListener); + customSeekG.setOnSeekBarChangeListener(customColorRgbListener); + customSeekB.setOnSeekBarChangeListener(customColorRgbListener); + if (customSeekA.getVisibility() == View.VISIBLE) { + customSeekA.setOnSeekBarChangeListener(customColorRgbListener); + customColorHex.setText(String.format("%08X", selectedCustomColor)); + } else { + customColorHex.setText(String.format("%06X", 0xFFFFFF & selectedCustomColor)); + } + } else { + dialog.setTitle(getBuilder().title); + dialog.setActionButton(DialogAction.NEUTRAL, getBuilder().customBtn); + if (isInSub()) { + dialog.setActionButton(DialogAction.NEGATIVE, getBuilder().backBtn); + } else { + dialog.setActionButton(DialogAction.NEGATIVE, getBuilder().cancelBtn); + } + grid.setVisibility(View.VISIBLE); + colorChooserCustomFrame.setVisibility(View.GONE); + customColorHex.removeTextChangedListener(customColorTextWatcher); + customColorTextWatcher = null; + customSeekR.setOnSeekBarChangeListener(null); + customSeekG.setOnSeekBarChangeListener(null); + customSeekB.setOnSeekBarChangeListener(null); + customColorRgbListener = null; + } + } + + private void invalidate() { + if (grid.getAdapter() == null) { + grid.setAdapter(new ColorGridAdapter()); + grid.setSelector( + ResourcesCompat.getDrawable(getResources(), R.drawable.bg_transparent, null)); + } else { + ((BaseAdapter) grid.getAdapter()).notifyDataSetChanged(); + } + if (getDialog() != null) { + getDialog().setTitle(getTitle()); + } + } + + private Builder getBuilder() { + if (getArguments() == null || !getArguments().containsKey("builder")) { + return null; + } + return (Builder) getArguments().getSerializable("builder"); + } + + private void dismissIfNecessary(FragmentManager fragmentManager, String tag) { + Fragment frag = fragmentManager.findFragmentByTag(tag); + if (frag != null) { + ((DialogFragment) frag).dismiss(); + fragmentManager.beginTransaction().remove(frag).commit(); + } + } + + @NonNull + public ColorChooserDialog show(FragmentManager fragmentManager) { + String tag; + Builder builder = getBuilder(); + if (builder.colorsTop != null) { + tag = TAG_CUSTOM; + } else { + tag = TAG_PRIMARY; + } + dismissIfNecessary(fragmentManager, tag); + show(fragmentManager, tag); + return this; + } + + @NonNull + public ColorChooserDialog show(FragmentActivity fragmentActivity) { + return show(fragmentActivity.getSupportFragmentManager()); + } + + @Retention(RetentionPolicy.SOURCE) + @StringDef({TAG_PRIMARY, TAG_ACCENT, TAG_CUSTOM}) + public @interface ColorChooserTag { + } + + public interface ColorCallback { + + void onColorSelection(@NonNull ColorChooserDialog dialog, @ColorInt int selectedColor); + + void onColorChooserDismissed(@NonNull ColorChooserDialog dialog); + } + + public static class Builder implements Serializable { + + @NonNull + final transient Context context; + @StringRes + final int title; + @Nullable + String mediumFont; + @Nullable + String regularFont; + @StringRes + int titleSub; + @ColorInt + int preselectColor; + @StringRes + int doneBtn = R.string.md_done_label; + @StringRes + int backBtn = R.string.md_back_label; + @StringRes + int cancelBtn = R.string.md_cancel_label; + @StringRes + int customBtn = R.string.md_custom_label; + @StringRes + int presetsBtn = R.string.md_presets_label; + @Nullable + int[] colorsTop; + @Nullable + int[][] colorsSub; + @Nullable + String tag; + @Nullable + Theme theme; + + ColorCallback callback; + + boolean dynamicButtonColor = true; + boolean allowUserCustom = true; + boolean allowUserCustomAlpha = true; + boolean setPreselectionColor = false; + + public Builder(@NonNull Context context, @StringRes int title) { + this.context = context; + this.title = title; + } + + @NonNull + public Builder typeface(@Nullable String medium, @Nullable String regular) { + this.mediumFont = medium; + this.regularFont = regular; + return this; + } + + @NonNull + public Builder titleSub(@StringRes int titleSub) { + this.titleSub = titleSub; + return this; + } + + @NonNull + public Builder tag(@Nullable String tag) { + this.tag = tag; + return this; + } + + @NonNull + public Builder theme(@NonNull Theme theme) { + this.theme = theme; + return this; + } + + @NonNull + public Builder preselect(@ColorInt int preselect) { + preselectColor = preselect; + setPreselectionColor = true; + return this; + } + + @NonNull + public Builder doneButton(@StringRes int text) { + doneBtn = text; + return this; + } + + @NonNull + public Builder backButton(@StringRes int text) { + backBtn = text; + return this; + } + + @NonNull + public Builder cancelButton(@StringRes int text) { + cancelBtn = text; + return this; + } + + @NonNull + public Builder customButton(@StringRes int text) { + customBtn = text; + return this; + } + + @NonNull + public Builder presetsButton(@StringRes int text) { + presetsBtn = text; + return this; + } + + @NonNull + public Builder dynamicButtonColor(boolean enabled) { + dynamicButtonColor = enabled; + return this; + } + + @NonNull + public Builder customColors(@NonNull int[] topLevel, @Nullable int[][] subLevel) { + colorsTop = topLevel; + colorsSub = subLevel; + return this; + } + + @NonNull + public Builder customColors(@ArrayRes int topLevel, @Nullable int[][] subLevel) { + colorsTop = DialogUtils.getColorArray(context, topLevel); + colorsSub = subLevel; + return this; + } + + @NonNull + public Builder allowUserColorInput(boolean allow) { + allowUserCustom = allow; + return this; + } + + @NonNull + public Builder allowUserColorInputAlpha(boolean allow) { + allowUserCustomAlpha = allow; + return this; + } + + @NonNull + public ColorChooserDialog build() { + ColorChooserDialog dialog = new ColorChooserDialog(); + Bundle args = new Bundle(); + args.putSerializable("builder", this); + dialog.setArguments(args); + return dialog; + } + + public Builder callback(ColorCallback callback) { + this.callback = callback; + return this; + } + + @NonNull + public ColorChooserDialog show(FragmentManager fragmentManager) { + ColorChooserDialog dialog = build(); + dialog.show(fragmentManager); + return dialog; + } + + @NonNull + public ColorChooserDialog show(FragmentActivity fragmentActivity) { + return show(fragmentActivity.getSupportFragmentManager()); + } + } + + private class ColorGridAdapter extends BaseAdapter { + + ColorGridAdapter() { + } + + @Override + public int getCount() { + if (isInSub()) { + return colorsSub[topIndex()].length; + } else { + return colorsTop.length; + } + } + + @Override + public Object getItem(int position) { + if (isInSub()) { + return colorsSub[topIndex()][position]; + } else { + return colorsTop[position]; + } + } + + @Override + public long getItemId(int position) { + return position; + } + + @SuppressLint("DefaultLocale") + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = new CircleView(getContext()); + convertView.setLayoutParams(new GridView.LayoutParams(circleSize, circleSize)); + } + CircleView child = (CircleView) convertView; + @ColorInt final int color = isInSub() ? colorsSub[topIndex()][position] : colorsTop[position]; + child.setBackgroundColor(color); + if (isInSub()) { + child.setSelected(subIndex() == position); + } else { + child.setSelected(topIndex() == position); + } + child.setTag(String.format("%d:%d", position, color)); + child.setOnClickListener(ColorChooserDialog.this); + child.setOnLongClickListener(ColorChooserDialog.this); + return convertView; + } + } +} diff --git a/app/src/main/res/drawable/bg_transparent.xml b/app/src/main/res/drawable/bg_transparent.xml new file mode 100644 index 000000000..261a644cd --- /dev/null +++ b/app/src/main/res/drawable/bg_transparent.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@android:color/transparent" /> +</shape> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_colorchooser.xml b/app/src/main/res/layout/dialog_colorchooser.xml new file mode 100644 index 000000000..53dde1694 --- /dev/null +++ b/app/src/main/res/layout/dialog_colorchooser.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <include layout="@layout/dialog_colorchooser_presets" /> + + <include + layout="@layout/dialog_colorchooser_custom" + android:visibility="gone" /> + + </FrameLayout> + +</ScrollView> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_colorchooser_custom.xml b/app/src/main/res/layout/dialog_colorchooser_custom.xml new file mode 100644 index 000000000..6417ccbbd --- /dev/null +++ b/app/src/main/res/layout/dialog_colorchooser_custom.xml @@ -0,0 +1,236 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/md_colorChooserCustomFrame" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="@dimen/md_title_frame_margin_bottom" + android:paddingTop="@dimen/md_title_frame_margin_bottom"> + + <View + android:id="@+id/md_colorIndicator" + android:layout_width="match_parent" + android:layout_height="120dp" + tools:background="@color/md_material_blue_600" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="@dimen/md_title_frame_margin_bottom" + android:gravity="center"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="2dp" + android:layout_marginRight="2dp" + android:digits="0123456789abcdefABCDEF" + android:text="#" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_title_textsize" + tools:ignore="HardcodedText,TextViewEdits" /> + + <EditText + android:id="@+id/md_hexInput" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:digits="0123456789abcdefABCDEF" + android:focusable="true" + android:hint="FF0099CC" + android:textColor="?colorTextPrimary" + android:textColorHint="?colorTextSecondary" + android:textSize="@dimen/md_title_textsize" + tools:ignore="HardcodedText" /> + + </LinearLayout> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/md_title_frame_margin_bottom" + android:paddingLeft="@dimen/md_dialog_frame_margin" + android:paddingRight="@dimen/md_dialog_frame_margin"> + + <!-- Alpha --> + + <TextView + android:id="@+id/md_colorALabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorA" + android:layout_alignTop="@+id/md_colorA" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:text="A" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + <SeekBar + android:id="@+id/md_colorA" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/md_title_frame_margin_bottom" + android:layout_toEndOf="@+id/md_colorALabel" + android:layout_toLeftOf="@+id/md_colorAValue" + android:layout_toRightOf="@+id/md_colorALabel" + android:layout_toStartOf="@+id/md_colorAValue" + android:focusable="true" + android:max="255" /> + + <TextView + android:id="@+id/md_colorAValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorA" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_alignTop="@+id/md_colorA" + android:layout_marginLeft="4dp" + android:layout_marginStart="4dp" + android:gravity="center" + android:minWidth="24dp" + android:text="0" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + <!-- Red --> + + <TextView + android:id="@+id/md_colorRLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorR" + android:layout_alignTop="@+id/md_colorR" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:text="R" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + <SeekBar + android:id="@+id/md_colorR" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/md_colorA" + android:layout_marginTop="@dimen/md_title_frame_margin_bottom" + android:layout_toEndOf="@+id/md_colorRLabel" + android:layout_toLeftOf="@+id/md_colorRValue" + android:layout_toRightOf="@+id/md_colorRLabel" + android:layout_toStartOf="@+id/md_colorRValue" + android:focusable="true" + android:max="255" /> + + <TextView + android:id="@+id/md_colorRValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorR" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_alignTop="@+id/md_colorR" + android:layout_marginLeft="4dp" + android:layout_marginStart="4dp" + android:gravity="center" + android:minWidth="24dp" + android:text="0" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + <!-- Green --> + + <TextView + android:id="@+id/md_colorGLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorG" + android:layout_alignTop="@+id/md_colorG" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:text="G" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + <SeekBar + android:id="@+id/md_colorG" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/md_colorR" + android:layout_marginTop="@dimen/md_title_frame_margin_bottom" + android:layout_toEndOf="@+id/md_colorGLabel" + android:layout_toLeftOf="@+id/md_colorGValue" + android:layout_toRightOf="@+id/md_colorGLabel" + android:layout_toStartOf="@+id/md_colorGValue" + android:focusable="true" + android:max="255" /> + + <TextView + android:id="@+id/md_colorGValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorG" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_alignTop="@+id/md_colorG" + android:layout_marginLeft="4dp" + android:layout_marginStart="4dp" + android:gravity="center" + android:minWidth="24dp" + android:text="0" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + <!-- Blue --> + + <TextView + android:id="@+id/md_colorBLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorB" + android:layout_alignTop="@+id/md_colorB" + android:layout_marginEnd="4dp" + android:layout_marginRight="4dp" + android:text="B" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + <SeekBar + android:id="@+id/md_colorB" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/md_colorG" + android:layout_marginTop="@dimen/md_title_frame_margin_bottom" + android:layout_toEndOf="@+id/md_colorBLabel" + android:layout_toLeftOf="@+id/md_colorBValue" + android:layout_toRightOf="@+id/md_colorBLabel" + android:layout_toStartOf="@+id/md_colorBValue" + android:max="255" /> + + <TextView + android:id="@+id/md_colorBValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBottom="@+id/md_colorB" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_alignTop="@+id/md_colorB" + android:layout_marginLeft="4dp" + android:layout_marginStart="4dp" + android:gravity="center" + android:minWidth="24dp" + android:text="0" + android:textColor="?colorTextPrimary" + android:textSize="@dimen/md_content_textsize" + tools:ignore="HardcodedText" /> + + </RelativeLayout> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_colorchooser_presets.xml b/app/src/main/res/layout/dialog_colorchooser_presets.xml new file mode 100644 index 000000000..5efc0a4ec --- /dev/null +++ b/app/src/main/res/layout/dialog_colorchooser_presets.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.afollestad.materialdialogs.color.FillGridView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/md_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + android:columnWidth="@dimen/colorchooser_circlesize" + android:gravity="center" + android:horizontalSpacing="8dp" + android:numColumns="auto_fit" + android:orientation="vertical" + android:padding="16dp" + android:stretchMode="columnWidth" + android:verticalSpacing="8dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_history.xml b/app/src/main/res/layout/layout_history.xml index e42496899..2a35012d5 100644 --- a/app/src/main/res/layout/layout_history.xml +++ b/app/src/main/res/layout/layout_history.xml @@ -1,57 +1,59 @@ <?xml version="1.0" encoding="utf-8"?> -<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout 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/card_panel" - style="?attr/cardStyle" android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:layout_marginBottom="16dp" - android:layout_marginLeft="16dp" - android:layout_marginRight="16dp" - android:layout_marginTop="16dp" - app:cardBackgroundColor="?attr/colorBackgroundCard"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> + android:layout_height="fill_parent"> + + <android.support.v7.widget.CardView + style="?attr/cardStyle" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layout_marginBottom="16dp" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + android:layout_marginTop="16dp" + app:cardBackgroundColor="?attr/colorBackgroundCard"> <LinearLayout android:layout_width="match_parent" - android:layout_height="48dp" - android:orientation="horizontal"> - - <TextView - android:layout_width="0dip" - android:layout_height="match_parent" - android:layout_weight="1" - android:gravity="center_vertical|fill_horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp" - android:text="@string/label_input_history" - android:textAppearance="@style/TextAppearance.AppCompat.Body2" - android:textColor="?attr/colorForegroundSecondary" /> + android:layout_height="match_parent" + android:orientation="vertical"> - <android.support.v7.widget.AppCompatImageView - android:id="@+id/close" - android:layout_width="48dp" + <LinearLayout + android:layout_width="match_parent" android:layout_height="48dp" - android:layout_gravity="top" - android:background="?attr/selectableItemBackgroundBorderless" - android:padding="12dp" - android:scaleType="fitXY" - app:srcCompat="@drawable/ic_close" - app:tint="?attr/colorForegroundSecondary" /> - + android:orientation="horizontal"> + + <TextView + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="center_vertical|fill_horizontal" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:text="@string/label_input_history" + android:textAppearance="@style/TextAppearance.AppCompat.Body2" + android:textColor="?attr/colorForegroundSecondary" /> + + <android.support.v7.widget.AppCompatImageView + android:id="@+id/send" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_gravity="top" + android:background="?attr/selectableItemBackgroundBorderless" + android:padding="12dp" + android:scaleType="fitXY" + app:srcCompat="@drawable/ic_close" + app:tint="?attr/colorForegroundSecondary" /> + </LinearLayout> + + <android.support.v7.widget.RecyclerView + android:id="@+id/msg_history" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + tools:listitem="@layout/widget_history_message" /> </LinearLayout> - - <android.support.v7.widget.RecyclerView - android:id="@+id/msg_history" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - tools:listitem="@layout/widget_history_message" /> - - </LinearLayout> - -</android.support.v7.widget.CardView> \ No newline at end of file + </android.support.v7.widget.CardView> +</FrameLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/widget_formatting.xml b/app/src/main/res/layout/widget_formatting.xml index e619482d9..ba916fcea 100644 --- a/app/src/main/res/layout/widget_formatting.xml +++ b/app/src/main/res/layout/widget_formatting.xml @@ -76,7 +76,7 @@ app:tint="?colorControlNormal" /> <View - android:id="@+id/ic_format_foreground_preview" + android:id="@+id/action_format_foreground_preview" android:layout_width="match_parent" android:layout_height="4dp" android:layout_gravity="center_horizontal|bottom" @@ -105,7 +105,7 @@ app:tint="?colorControlNormal" /> <View - android:id="@+id/ic_format_background_preview" + android:id="@+id/action_format_background_preview" android:layout_width="match_parent" android:layout_height="4dp" android:layout_gravity="center_horizontal|bottom" diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 26f6295db..1ae9cfc6e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -8,9 +8,12 @@ <string name="connection_service_description">Hält eine Verbindung zum Core, um Benachrichtigungen und die Übertragung von Nachrichten zu ermöglichen</string> <string name="label_about">Über</string> + <string name="label_back">Zurück</string> <string name="label_buffer_name">Chatname</string> <string name="label_cancel">Abbrechen</string> <string name="label_close">Schließen</string> + <string name="label_colors_custom">Anpassen</string> + <string name="label_colors_mirc">mIRC</string> <string name="label_connect">Verbinden</string> <string name="label_contributors">Mitwirkende</string> <string name="label_copy">Kopieren</string> @@ -29,12 +32,13 @@ <string name="label_license">Lizenz</string> <string name="label_new_account">Account hinzufügen</string> <string name="label_no">Nein</string> + <string name="label_reset">Zurücksetzen</string> <string name="label_open">Öffnen</string> <string name="label_part">Verlassen</string> <string name="label_placeholder">Nachricht schreiben…</string> <string name="label_rename">Umbenennen</string> <string name="label_save">Speichern</string> - <string name="label_select_multiple">Auswählen</string> + <string name="label_select">Auswählen</string> <string name="label_settings">Einstellungen</string> <string name="label_share">Teilen</string> <string name="label_share_crashreport">Absturzbericht Teilen</string> diff --git a/app/src/main/res/values-large/dimens.xml b/app/src/main/res/values-large/dimens.xml new file mode 100644 index 000000000..e9d3194ca --- /dev/null +++ b/app/src/main/res/values-large/dimens.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="colorchooser_circlesize">64dp</dimen> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 685b30aea..5535c2b7c 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -9,4 +9,6 @@ <dimen name="max_content_width">480dp</dimen> <dimen name="button_corner_radius">2dp</dimen> + + <dimen name="colorchooser_circlesize">56dp</dimen> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 91c25cd95..86c826ad9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,9 +8,12 @@ <string name="connection_service_description">Keeps a connection to your core to allow for notifications, and to transmit messages.</string> <string name="label_about">About</string> + <string name="label_back">Back</string> <string name="label_buffer_name">Buffer Name</string> <string name="label_cancel">Cancel</string> <string name="label_close">Close</string> + <string name="label_colors_custom">Custom</string> + <string name="label_colors_mirc">mIRC</string> <string name="label_connect">Connect</string> <string name="label_contributors">Contributors</string> <string name="label_copy">Copy</string> @@ -29,12 +32,13 @@ <string name="label_license">License</string> <string name="label_new_account">New Account</string> <string name="label_no">No</string> + <string name="label_reset">Reset</string> <string name="label_open">Open</string> <string name="label_part">Part</string> <string name="label_placeholder">Write a message…</string> <string name="label_rename">Rename</string> <string name="label_save">Save</string> - <string name="label_select_multiple">Select</string> + <string name="label_select">Select</string> <string name="label_settings">Settings</string> <string name="label_share">Share</string> <string name="label_share_crashreport">Share Crash Report</string> diff --git a/app/src/main/res/values/themes_base.xml b/app/src/main/res/values/themes_base.xml index 8d37a28cd..90530c4b2 100644 --- a/app/src/main/res/values/themes_base.xml +++ b/app/src/main/res/values/themes_base.xml @@ -83,78 +83,91 @@ <item name="mircColor13">#ff00ff</item> <item name="mircColor14">#808080</item> <item name="mircColor15">#c0c0c0</item> + <item name="mircColor16">#470000</item> - <item name="mircColor17">#472100</item> - <item name="mircColor18">#474700</item> - <item name="mircColor19">#324700</item> - <item name="mircColor20">#004700</item> - <item name="mircColor21">#00472c</item> - <item name="mircColor22">#004747</item> - <item name="mircColor23">#002747</item> - <item name="mircColor24">#000047</item> - <item name="mircColor25">#2e0047</item> - <item name="mircColor26">#470047</item> - <item name="mircColor27">#47002a</item> <item name="mircColor28">#740000</item> - <item name="mircColor29">#743a00</item> - <item name="mircColor30">#747400</item> - <item name="mircColor31">#517400</item> - <item name="mircColor32">#007400</item> - <item name="mircColor33">#007449</item> - <item name="mircColor34">#007474</item> - <item name="mircColor35">#004074</item> - <item name="mircColor36">#000074</item> - <item name="mircColor37">#4b0074</item> - <item name="mircColor38">#740074</item> - <item name="mircColor39">#740045</item> <item name="mircColor40">#b50000</item> - <item name="mircColor41">#b56300</item> - <item name="mircColor42">#b5b500</item> - <item name="mircColor43">#7db500</item> - <item name="mircColor44">#00b500</item> - <item name="mircColor45">#00b571</item> - <item name="mircColor46">#00b5b5</item> - <item name="mircColor47">#0063b5</item> - <item name="mircColor48">#0000b5</item> - <item name="mircColor49">#7500b5</item> - <item name="mircColor50">#b500b5</item> - <item name="mircColor51">#b5006b</item> <item name="mircColor52">#ff0000</item> - <item name="mircColor53">#ff8c00</item> - <item name="mircColor54">#ffff00</item> - <item name="mircColor55">#b2ff00</item> - <item name="mircColor56">#00ff00</item> - <item name="mircColor57">#00ffa0</item> - <item name="mircColor58">#00ffff</item> - <item name="mircColor59">#008cff</item> - <item name="mircColor60">#0000ff</item> - <item name="mircColor61">#a500ff</item> - <item name="mircColor62">#ff00ff</item> - <item name="mircColor63">#ff0098</item> <item name="mircColor64">#ff5959</item> - <item name="mircColor65">#ffb459</item> - <item name="mircColor66">#ffff71</item> - <item name="mircColor67">#cfff60</item> - <item name="mircColor68">#6fff6f</item> - <item name="mircColor69">#65ffc9</item> - <item name="mircColor70">#6dffff</item> - <item name="mircColor71">#59b4ff</item> - <item name="mircColor72">#5959ff</item> - <item name="mircColor73">#c459ff</item> - <item name="mircColor74">#ff66ff</item> - <item name="mircColor75">#ff59bc</item> <item name="mircColor76">#ff9c9c</item> + + <item name="mircColor17">#472100</item> + <item name="mircColor29">#743a00</item> + <item name="mircColor41">#b56300</item> + <item name="mircColor53">#ff8c00</item> + <item name="mircColor65">#ffb459</item> <item name="mircColor77">#ffd39c</item> + + <item name="mircColor18">#474700</item> + <item name="mircColor30">#747400</item> + <item name="mircColor42">#b5b500</item> + <item name="mircColor54">#ffff00</item> + <item name="mircColor66">#ffff71</item> <item name="mircColor78">#ffff9c</item> + + <item name="mircColor19">#324700</item> + <item name="mircColor31">#517400</item> + <item name="mircColor43">#7db500</item> + <item name="mircColor55">#b2ff00</item> + <item name="mircColor67">#cfff60</item> <item name="mircColor79">#e2ff9c</item> + + <item name="mircColor20">#004700</item> + <item name="mircColor32">#007400</item> + <item name="mircColor44">#00b500</item> + <item name="mircColor56">#00ff00</item> + <item name="mircColor68">#6fff6f</item> <item name="mircColor80">#9cff9c</item> + + <item name="mircColor21">#00472c</item> + <item name="mircColor33">#007449</item> + <item name="mircColor45">#00b571</item> + <item name="mircColor57">#00ffa0</item> + <item name="mircColor69">#65ffc9</item> <item name="mircColor81">#9cffdb</item> + + <item name="mircColor22">#004747</item> + <item name="mircColor34">#007474</item> + <item name="mircColor46">#00b5b5</item> + <item name="mircColor58">#00ffff</item> + <item name="mircColor70">#6dffff</item> <item name="mircColor82">#9cffff</item> + + <item name="mircColor23">#002747</item> + <item name="mircColor35">#004074</item> + <item name="mircColor47">#0063b5</item> + <item name="mircColor59">#008cff</item> + <item name="mircColor71">#59b4ff</item> <item name="mircColor83">#9cd3ff</item> + + <item name="mircColor24">#000047</item> + <item name="mircColor36">#000074</item> + <item name="mircColor48">#0000b5</item> + <item name="mircColor60">#0000ff</item> + <item name="mircColor72">#5959ff</item> <item name="mircColor84">#9c9cff</item> + + <item name="mircColor25">#2e0047</item> + <item name="mircColor37">#4b0074</item> + <item name="mircColor49">#7500b5</item> + <item name="mircColor61">#a500ff</item> + <item name="mircColor73">#c459ff</item> <item name="mircColor85">#dc9cff</item> + + <item name="mircColor26">#470047</item> + <item name="mircColor38">#740074</item> + <item name="mircColor50">#b500b5</item> + <item name="mircColor62">#ff00ff</item> + <item name="mircColor74">#ff66ff</item> <item name="mircColor86">#ff9cff</item> + + <item name="mircColor27">#47002a</item> + <item name="mircColor39">#740045</item> + <item name="mircColor51">#b5006b</item> + <item name="mircColor63">#ff0098</item> + <item name="mircColor75">#ff59bc</item> <item name="mircColor87">#ff94d3</item> + <item name="mircColor88">#000000</item> <item name="mircColor89">#131313</item> <item name="mircColor90">#282828</item> @@ -217,78 +230,91 @@ <item name="mircColor13">#ff00ff</item> <item name="mircColor14">#808080</item> <item name="mircColor15">#c0c0c0</item> + <item name="mircColor16">#470000</item> - <item name="mircColor17">#472100</item> - <item name="mircColor18">#474700</item> - <item name="mircColor19">#324700</item> - <item name="mircColor20">#004700</item> - <item name="mircColor21">#00472c</item> - <item name="mircColor22">#004747</item> - <item name="mircColor23">#002747</item> - <item name="mircColor24">#000047</item> - <item name="mircColor25">#2e0047</item> - <item name="mircColor26">#470047</item> - <item name="mircColor27">#47002a</item> <item name="mircColor28">#740000</item> - <item name="mircColor29">#743a00</item> - <item name="mircColor30">#747400</item> - <item name="mircColor31">#517400</item> - <item name="mircColor32">#007400</item> - <item name="mircColor33">#007449</item> - <item name="mircColor34">#007474</item> - <item name="mircColor35">#004074</item> - <item name="mircColor36">#000074</item> - <item name="mircColor37">#4b0074</item> - <item name="mircColor38">#740074</item> - <item name="mircColor39">#740045</item> <item name="mircColor40">#b50000</item> - <item name="mircColor41">#b56300</item> - <item name="mircColor42">#b5b500</item> - <item name="mircColor43">#7db500</item> - <item name="mircColor44">#00b500</item> - <item name="mircColor45">#00b571</item> - <item name="mircColor46">#00b5b5</item> - <item name="mircColor47">#0063b5</item> - <item name="mircColor48">#0000b5</item> - <item name="mircColor49">#7500b5</item> - <item name="mircColor50">#b500b5</item> - <item name="mircColor51">#b5006b</item> <item name="mircColor52">#ff0000</item> - <item name="mircColor53">#ff8c00</item> - <item name="mircColor54">#ffff00</item> - <item name="mircColor55">#b2ff00</item> - <item name="mircColor56">#00ff00</item> - <item name="mircColor57">#00ffa0</item> - <item name="mircColor58">#00ffff</item> - <item name="mircColor59">#008cff</item> - <item name="mircColor60">#0000ff</item> - <item name="mircColor61">#a500ff</item> - <item name="mircColor62">#ff00ff</item> - <item name="mircColor63">#ff0098</item> <item name="mircColor64">#ff5959</item> - <item name="mircColor65">#ffb459</item> - <item name="mircColor66">#ffff71</item> - <item name="mircColor67">#cfff60</item> - <item name="mircColor68">#6fff6f</item> - <item name="mircColor69">#65ffc9</item> - <item name="mircColor70">#6dffff</item> - <item name="mircColor71">#59b4ff</item> - <item name="mircColor72">#5959ff</item> - <item name="mircColor73">#c459ff</item> - <item name="mircColor74">#ff66ff</item> - <item name="mircColor75">#ff59bc</item> <item name="mircColor76">#ff9c9c</item> + + <item name="mircColor17">#472100</item> + <item name="mircColor29">#743a00</item> + <item name="mircColor41">#b56300</item> + <item name="mircColor53">#ff8c00</item> + <item name="mircColor65">#ffb459</item> <item name="mircColor77">#ffd39c</item> + + <item name="mircColor18">#474700</item> + <item name="mircColor30">#747400</item> + <item name="mircColor42">#b5b500</item> + <item name="mircColor54">#ffff00</item> + <item name="mircColor66">#ffff71</item> <item name="mircColor78">#ffff9c</item> + + <item name="mircColor19">#324700</item> + <item name="mircColor31">#517400</item> + <item name="mircColor43">#7db500</item> + <item name="mircColor55">#b2ff00</item> + <item name="mircColor67">#cfff60</item> <item name="mircColor79">#e2ff9c</item> + + <item name="mircColor20">#004700</item> + <item name="mircColor32">#007400</item> + <item name="mircColor44">#00b500</item> + <item name="mircColor56">#00ff00</item> + <item name="mircColor68">#6fff6f</item> <item name="mircColor80">#9cff9c</item> + + <item name="mircColor21">#00472c</item> + <item name="mircColor33">#007449</item> + <item name="mircColor45">#00b571</item> + <item name="mircColor57">#00ffa0</item> + <item name="mircColor69">#65ffc9</item> <item name="mircColor81">#9cffdb</item> + + <item name="mircColor22">#004747</item> + <item name="mircColor34">#007474</item> + <item name="mircColor46">#00b5b5</item> + <item name="mircColor58">#00ffff</item> + <item name="mircColor70">#6dffff</item> <item name="mircColor82">#9cffff</item> + + <item name="mircColor23">#002747</item> + <item name="mircColor35">#004074</item> + <item name="mircColor47">#0063b5</item> + <item name="mircColor59">#008cff</item> + <item name="mircColor71">#59b4ff</item> <item name="mircColor83">#9cd3ff</item> + + <item name="mircColor24">#000047</item> + <item name="mircColor36">#000074</item> + <item name="mircColor48">#0000b5</item> + <item name="mircColor60">#0000ff</item> + <item name="mircColor72">#5959ff</item> <item name="mircColor84">#9c9cff</item> + + <item name="mircColor25">#2e0047</item> + <item name="mircColor37">#4b0074</item> + <item name="mircColor49">#7500b5</item> + <item name="mircColor61">#a500ff</item> + <item name="mircColor73">#c459ff</item> <item name="mircColor85">#dc9cff</item> + + <item name="mircColor26">#470047</item> + <item name="mircColor38">#740074</item> + <item name="mircColor50">#b500b5</item> + <item name="mircColor62">#ff00ff</item> + <item name="mircColor74">#ff66ff</item> <item name="mircColor86">#ff9cff</item> + + <item name="mircColor27">#47002a</item> + <item name="mircColor39">#740045</item> + <item name="mircColor51">#b5006b</item> + <item name="mircColor63">#ff0098</item> + <item name="mircColor75">#ff59bc</item> <item name="mircColor87">#ff94d3</item> + <item name="mircColor88">#000000</item> <item name="mircColor89">#131313</item> <item name="mircColor90">#282828</item> -- GitLab