|
@@ -21,72 +21,191 @@
|
|
|
|
|
|
package com.nextcloud.talk.ui.theme
|
|
|
|
|
|
+import android.annotation.TargetApi
|
|
|
+import android.app.Activity
|
|
|
import android.content.Context
|
|
|
import android.content.res.ColorStateList
|
|
|
import android.graphics.Color
|
|
|
import android.graphics.PorterDuff
|
|
|
import android.graphics.drawable.Drawable
|
|
|
+import android.graphics.drawable.LayerDrawable
|
|
|
+import android.os.Build
|
|
|
+import android.text.Spannable
|
|
|
+import android.text.SpannableString
|
|
|
+import android.view.MenuItem
|
|
|
import android.view.View
|
|
|
+import android.view.ViewGroup
|
|
|
+import android.widget.Button
|
|
|
import android.widget.CheckBox
|
|
|
import android.widget.EditText
|
|
|
+import android.widget.ImageButton
|
|
|
import android.widget.ImageView
|
|
|
+import android.widget.LinearLayout
|
|
|
import android.widget.ProgressBar
|
|
|
import android.widget.RadioButton
|
|
|
import android.widget.SeekBar
|
|
|
import android.widget.TextView
|
|
|
import androidx.annotation.ColorInt
|
|
|
+import androidx.annotation.DrawableRes
|
|
|
import androidx.appcompat.content.res.AppCompatResources
|
|
|
+import androidx.appcompat.widget.SearchView
|
|
|
+import androidx.appcompat.widget.SearchView.SearchAutoComplete
|
|
|
import androidx.appcompat.widget.SwitchCompat
|
|
|
import androidx.core.content.ContextCompat
|
|
|
import androidx.core.content.res.ResourcesCompat
|
|
|
+import androidx.core.graphics.ColorUtils
|
|
|
+import androidx.core.graphics.drawable.DrawableCompat
|
|
|
+import androidx.core.view.ViewCompat
|
|
|
import androidx.core.view.children
|
|
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
|
+import com.google.android.material.appbar.MaterialToolbar
|
|
|
import com.google.android.material.button.MaterialButton
|
|
|
import com.google.android.material.card.MaterialCardView
|
|
|
import com.google.android.material.chip.Chip
|
|
|
+import com.google.android.material.chip.ChipDrawable
|
|
|
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
|
|
+import com.google.android.material.shape.MaterialShapeDrawable
|
|
|
import com.google.android.material.tabs.TabLayout
|
|
|
import com.google.android.material.textfield.TextInputLayout
|
|
|
+import com.google.android.material.textview.MaterialTextView
|
|
|
import com.nextcloud.talk.R
|
|
|
+import com.nextcloud.talk.utils.DisplayUtils
|
|
|
import com.nextcloud.talk.utils.DrawableUtils
|
|
|
import com.nextcloud.talk.utils.ui.ColorUtil
|
|
|
import com.nextcloud.talk.utils.ui.PlatformThemeUtil.isDarkMode
|
|
|
+import com.vanniktech.emoji.EmojiTextView
|
|
|
import com.yarolegovich.mp.MaterialPreferenceCategory
|
|
|
import com.yarolegovich.mp.MaterialSwitchPreference
|
|
|
+import eu.davidea.flexibleadapter.utils.FlexibleUtils
|
|
|
+import scheme.Scheme
|
|
|
import javax.inject.Inject
|
|
|
+import kotlin.math.roundToInt
|
|
|
|
|
|
@Suppress("TooManyFunctions")
|
|
|
-class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private val colorUtil: ColorUtil) {
|
|
|
+class ViewThemeUtils @Inject constructor(private val schemes: MaterialSchemes, private val colorUtil: ColorUtil) {
|
|
|
|
|
|
/**
|
|
|
- * Color for painting elements
|
|
|
+ * Scheme for painting elements
|
|
|
*/
|
|
|
- fun getElementColor(context: Context): Int = when {
|
|
|
- isDarkMode(context) -> theme.colorElementDark
|
|
|
- else -> theme.colorElementBright
|
|
|
+ fun getScheme(context: Context): Scheme = when {
|
|
|
+ isDarkMode(context) -> schemes.darkScheme
|
|
|
+ else -> schemes.lightScheme
|
|
|
}
|
|
|
|
|
|
- private fun withElementColor(view: View, block: (Int) -> Unit) {
|
|
|
- block(getElementColor(view.context))
|
|
|
+ private fun getSchemeDark(): Scheme = schemes.darkScheme
|
|
|
+
|
|
|
+ private fun withScheme(view: View, block: (Scheme) -> Unit) {
|
|
|
+ block(getScheme(view.context))
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun withScheme(context: Context, block: (Scheme) -> Unit) {
|
|
|
+ block(getScheme(context))
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun withSchemeDark(block: (Scheme) -> Unit) {
|
|
|
+ block(getSchemeDark())
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeToolbar(toolbar: MaterialToolbar) {
|
|
|
+ withScheme(toolbar) { scheme ->
|
|
|
+ toolbar.setBackgroundColor(scheme.surface)
|
|
|
+ toolbar.setNavigationIconTint(scheme.onSurface)
|
|
|
+ toolbar.setTitleTextColor(scheme.onSurface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorViewBackground(view: View) {
|
|
|
+ withScheme(view) { scheme ->
|
|
|
+ view.setBackgroundColor(scheme.surface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorToolbarMenuIcon(context: Context, item: MenuItem) {
|
|
|
+ withScheme(context) { scheme ->
|
|
|
+ item.icon.setColorFilter(scheme.onSurface, PorterDuff.Mode.SRC_ATOP)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorToolbarOverflowIcon(toolbar: MaterialToolbar) {
|
|
|
+ withScheme(toolbar) { scheme ->
|
|
|
+ toolbar.overflowIcon?.setColorFilter(scheme.onSurface, PorterDuff.Mode.SRC_ATOP)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeSearchView(searchView: SearchView) {
|
|
|
+ withScheme(searchView) { scheme ->
|
|
|
+ // hacky as no default way is provided
|
|
|
+ val editText = searchView.findViewById<SearchAutoComplete>(R.id.search_src_text)
|
|
|
+ val searchPlate = searchView.findViewById<LinearLayout>(R.id.search_plate)
|
|
|
+ editText.textSize = SEARCH_TEXT_SIZE
|
|
|
+ editText.setHintTextColor(scheme.onSurfaceVariant)
|
|
|
+ editText.setTextColor(scheme.onSurface)
|
|
|
+ editText.setBackgroundColor(scheme.surface)
|
|
|
+ searchPlate.setBackgroundColor(scheme.surface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeSearchBarText(searchText: MaterialTextView) {
|
|
|
+ withScheme(searchText) { scheme ->
|
|
|
+ searchText.setHintTextColor(scheme.onSurfaceVariant)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeStatusBar(activity: Activity, view: View) {
|
|
|
+ withScheme(view) { scheme ->
|
|
|
+ DisplayUtils.applyColorToStatusBar(activity, scheme.surface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun resetStatusBar(activity: Activity, view: View) {
|
|
|
+ DisplayUtils.applyColorToStatusBar(
|
|
|
+ activity,
|
|
|
+ ResourcesCompat.getColor(
|
|
|
+ activity.resources,
|
|
|
+ R.color.bg_default,
|
|
|
+ activity.theme
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeDialog(view: View) {
|
|
|
+ withScheme(view) { scheme ->
|
|
|
+ view.setBackgroundColor(scheme.surface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeDialogDark(view: View) {
|
|
|
+ withSchemeDark { scheme ->
|
|
|
+ view.setBackgroundColor(scheme.surface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeDialogDivider(view: View) {
|
|
|
+ withScheme(view) { scheme ->
|
|
|
+ view.setBackgroundColor(scheme.surfaceVariant)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
fun themeFAB(fab: FloatingActionButton) {
|
|
|
- withElementColor(fab) { color ->
|
|
|
- fab.backgroundTintList = ColorStateList.valueOf(color)
|
|
|
- fab.imageTintList = ColorStateList.valueOf(theme.colorText)
|
|
|
+ withScheme(fab) { scheme ->
|
|
|
+ fab.backgroundTintList = ColorStateList.valueOf(scheme.primaryContainer)
|
|
|
+ fab.imageTintList = ColorStateList.valueOf(scheme.onPrimaryContainer)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun themeHorizontalSeekBar(seekBar: SeekBar) {
|
|
|
- withElementColor(seekBar) { color ->
|
|
|
- themeHorizontalSeekBar(seekBar, color)
|
|
|
+ fun themeCardView(cardView: MaterialCardView) {
|
|
|
+ withScheme(cardView) { scheme ->
|
|
|
+ cardView.backgroundTintList = ColorStateList.valueOf(scheme.surface)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun themeHorizontalSeekBar(seekBar: SeekBar, @ColorInt color: Int) {
|
|
|
- themeHorizontalProgressBar(seekBar, color)
|
|
|
- seekBar.thumb.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
|
|
+ fun themeHorizontalSeekBar(seekBar: SeekBar) {
|
|
|
+ withScheme(seekBar) { scheme ->
|
|
|
+ themeHorizontalProgressBar(seekBar, scheme.primary)
|
|
|
+ seekBar.thumb.setColorFilter(scheme.primary, PorterDuff.Mode.SRC_IN)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
fun themeHorizontalProgressBar(progressBar: ProgressBar?, @ColorInt color: Int) {
|
|
@@ -96,23 +215,50 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun colorTextViewElement(textView: TextView) {
|
|
|
- withElementColor(textView) { color ->
|
|
|
- textView.setTextColor(color)
|
|
|
+ fun colorPrimaryTextViewElement(textView: TextView) {
|
|
|
+ withScheme(textView) { scheme ->
|
|
|
+ textView.setTextColor(scheme.primary)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorPrimaryTextViewElementDarkMode(textView: TextView) {
|
|
|
+ withSchemeDark { scheme ->
|
|
|
+ textView.setTextColor(scheme.primary)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun colorTextViewText(textView: TextView) {
|
|
|
- textView.setTextColor(theme.colorText)
|
|
|
+ fun colorPrimaryView(view: View) {
|
|
|
+ withScheme(view) { scheme ->
|
|
|
+ view.setBackgroundColor(scheme.primary)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Colors the background as element color and the foreground as text color.
|
|
|
*/
|
|
|
fun colorImageViewButton(imageView: ImageView) {
|
|
|
- withElementColor(imageView) { color ->
|
|
|
- imageView.imageTintList = ColorStateList.valueOf(theme.colorText)
|
|
|
- imageView.backgroundTintList = ColorStateList.valueOf(color)
|
|
|
+ withScheme(imageView) { scheme ->
|
|
|
+ imageView.imageTintList = ColorStateList.valueOf(scheme.onPrimaryContainer)
|
|
|
+ imageView.backgroundTintList = ColorStateList.valueOf(scheme.primaryContainer)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeImageButton(imageButton: ImageButton) {
|
|
|
+ withScheme(imageButton) { scheme ->
|
|
|
+ imageButton.imageTintList = ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_selected),
|
|
|
+ intArrayOf(-android.R.attr.state_selected),
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.primary,
|
|
|
+ scheme.onSurfaceVariant,
|
|
|
+ scheme.onSurfaceVariant,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -120,61 +266,256 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
* Tints the image with element color
|
|
|
*/
|
|
|
fun colorImageView(imageView: ImageView) {
|
|
|
- withElementColor(imageView) { color ->
|
|
|
- imageView.imageTintList = ColorStateList.valueOf(color)
|
|
|
+ withScheme(imageView) { scheme ->
|
|
|
+ imageView.imageTintList = ColorStateList.valueOf(scheme.primary)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Tints the image with text color
|
|
|
- */
|
|
|
- fun colorImageViewText(imageView: ImageView) {
|
|
|
- imageView.imageTintList = ColorStateList.valueOf(theme.colorText)
|
|
|
+ fun colorOutgoingQuoteText(textView: TextView) {
|
|
|
+ withScheme(textView) { scheme ->
|
|
|
+ textView.setTextColor(scheme.onSurfaceVariant)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorOutgoingQuoteAuthorText(textView: TextView) {
|
|
|
+ withScheme(textView) { scheme ->
|
|
|
+ ColorUtils.setAlphaComponent(scheme.onSurfaceVariant, ALPHA_80_INT)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorOutgoingQuoteBackground(view: View) {
|
|
|
+ withScheme(view) { scheme ->
|
|
|
+ view.setBackgroundColor(scheme.onSurfaceVariant)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorMaterialTextButton(button: MaterialButton) {
|
|
|
+ withScheme(button) { scheme ->
|
|
|
+ button.rippleColor = ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_pressed)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ colorUtil.adjustOpacity(scheme.primary, SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
fun colorMaterialButtonText(button: MaterialButton) {
|
|
|
- withElementColor(button) { color ->
|
|
|
+ withScheme(button) { scheme ->
|
|
|
val disabledColor = ContextCompat.getColor(button.context, R.color.disabled_text)
|
|
|
val colorStateList = ColorStateList(
|
|
|
arrayOf(
|
|
|
intArrayOf(android.R.attr.state_enabled),
|
|
|
intArrayOf(-android.R.attr.state_enabled)
|
|
|
),
|
|
|
- intArrayOf(color, disabledColor)
|
|
|
+ intArrayOf(scheme.primary, disabledColor)
|
|
|
)
|
|
|
button.setTextColor(colorStateList)
|
|
|
button.iconTint = colorStateList
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun colorMaterialButtonBackground(button: MaterialButton) {
|
|
|
- withElementColor(button) { color ->
|
|
|
- button.setBackgroundColor(color)
|
|
|
+ fun colorTextButtons(vararg buttons: Button) {
|
|
|
+ withScheme(buttons[0]) { scheme ->
|
|
|
+ for (button in buttons) {
|
|
|
+ button.setTextColor(
|
|
|
+ ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.primary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- val disabledColor = ContextCompat.getColor(button.context, R.color.disabled_text)
|
|
|
- val colorStateList = ColorStateList(
|
|
|
+ fun colorMaterialButtonPrimaryFilled(button: MaterialButton) {
|
|
|
+ withScheme(button) { scheme ->
|
|
|
+ button.backgroundTintList =
|
|
|
+ ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.primary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ button.setTextColor(
|
|
|
+ ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.onPrimary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ button.iconTint = ColorStateList(
|
|
|
arrayOf(
|
|
|
intArrayOf(android.R.attr.state_enabled),
|
|
|
intArrayOf(-android.R.attr.state_enabled)
|
|
|
),
|
|
|
- intArrayOf(theme.colorText, disabledColor)
|
|
|
+ intArrayOf(
|
|
|
+ scheme.onPrimary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- button.setTextColor(colorStateList)
|
|
|
- button.iconTint = colorStateList
|
|
|
+ fun colorMaterialButtonPrimaryOutlined(button: MaterialButton) {
|
|
|
+ withScheme(button) { scheme ->
|
|
|
+ button.strokeColor = ColorStateList.valueOf(scheme.outline)
|
|
|
+ button.setTextColor(
|
|
|
+ ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.primary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+ button.iconTint = ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.primary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorMaterialButtonPrimaryBorderless(button: MaterialButton) {
|
|
|
+ withScheme(button) { scheme ->
|
|
|
+ button.setTextColor(
|
|
|
+ ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.primary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+ button.iconTint = ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_enabled),
|
|
|
+ intArrayOf(-android.R.attr.state_enabled)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ scheme.primary,
|
|
|
+ colorUtil.adjustOpacity(scheme.onSurface, ON_SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeIncomingMessageBubble(bubble: ViewGroup, grouped: Boolean, deleted: Boolean) {
|
|
|
+ val resources = bubble.resources
|
|
|
+
|
|
|
+ var bubbleResource = R.drawable.shape_incoming_message
|
|
|
+
|
|
|
+ if (grouped) {
|
|
|
+ bubbleResource = R.drawable.shape_grouped_incoming_message
|
|
|
+ }
|
|
|
+
|
|
|
+ val bgBubbleColor = if (deleted) {
|
|
|
+ resources.getColor(R.color.bg_message_list_incoming_bubble_deleted)
|
|
|
+ } else {
|
|
|
+ resources.getColor(R.color.bg_message_list_incoming_bubble)
|
|
|
+ }
|
|
|
+ val bubbleDrawable = DisplayUtils.getMessageSelector(
|
|
|
+ bgBubbleColor,
|
|
|
+ resources.getColor(R.color.transparent),
|
|
|
+ bgBubbleColor, bubbleResource
|
|
|
+ )
|
|
|
+ ViewCompat.setBackground(bubble, bubbleDrawable)
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themeOutgoingMessageBubble(bubble: ViewGroup, grouped: Boolean, deleted: Boolean) {
|
|
|
+ withScheme(bubble) { scheme ->
|
|
|
+ val bgBubbleColor = if (deleted) {
|
|
|
+ ColorUtils.setAlphaComponent(scheme.surfaceVariant, HALF_ALPHA_INT)
|
|
|
+ } else {
|
|
|
+ scheme.surfaceVariant
|
|
|
+ }
|
|
|
+
|
|
|
+ val layout = if (grouped) {
|
|
|
+ R.drawable.shape_grouped_outcoming_message
|
|
|
+ } else {
|
|
|
+ R.drawable.shape_outcoming_message
|
|
|
+ }
|
|
|
+ val bubbleDrawable = DisplayUtils.getMessageSelector(
|
|
|
+ bgBubbleColor,
|
|
|
+ ResourcesCompat.getColor(bubble.resources, R.color.transparent, null),
|
|
|
+ bgBubbleColor,
|
|
|
+ layout
|
|
|
+ )
|
|
|
+ ViewCompat.setBackground(bubble, bubbleDrawable)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fun colorCardViewBackground(card: MaterialCardView) {
|
|
|
- withElementColor(card) { color ->
|
|
|
- card.setCardBackgroundColor(color)
|
|
|
+ withScheme(card) { scheme ->
|
|
|
+ card.setCardBackgroundColor(scheme.surfaceVariant)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorContactChatItemName(contactName: androidx.emoji.widget.EmojiTextView) {
|
|
|
+ withScheme(contactName) { scheme ->
|
|
|
+ contactName.setTextColor(scheme.onPrimaryContainer)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorContactChatItemBackground(card: MaterialCardView) {
|
|
|
+ withScheme(card) { scheme ->
|
|
|
+ card.setCardBackgroundColor(scheme.primaryContainer)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorCircularProgressBarOnPrimaryContainer(progressBar: ProgressBar) {
|
|
|
+ withScheme(progressBar) { scheme ->
|
|
|
+ progressBar.indeterminateDrawable.setColorFilter(scheme.onPrimaryContainer, PorterDuff.Mode.SRC_ATOP)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorCircularProgressBar(progressBar: ProgressBar) {
|
|
|
+ withScheme(progressBar) { scheme ->
|
|
|
+ progressBar.indeterminateDrawable.setColorFilter(scheme.primary, PorterDuff.Mode.SRC_ATOP)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorCircularProgressBarOnSurfaceVariant(progressBar: ProgressBar) {
|
|
|
+ withScheme(progressBar) { scheme ->
|
|
|
+ progressBar.indeterminateDrawable.setColorFilter(scheme.onSurfaceVariant, PorterDuff.Mode.SRC_ATOP)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// TODO split this util into classes depending on framework views vs library views
|
|
|
fun colorPreferenceCategory(category: MaterialPreferenceCategory) {
|
|
|
- withElementColor(category) { color ->
|
|
|
- category.setTitleColor(color)
|
|
|
+ withScheme(category) { scheme ->
|
|
|
+ category.setTitleColor(scheme.primary)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -188,7 +529,7 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
}
|
|
|
|
|
|
fun colorSwitchCompat(switchCompat: SwitchCompat) {
|
|
|
- withElementColor(switchCompat) { color ->
|
|
|
+ withScheme(switchCompat) { scheme ->
|
|
|
|
|
|
val context = switchCompat.context
|
|
|
|
|
@@ -203,11 +544,16 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
context.theme
|
|
|
)
|
|
|
|
|
|
- val trackColor =
|
|
|
- Color.argb(SWITCHCOMPAT_TRACK_ALPHA, Color.red(color), Color.green(color), Color.blue(color))
|
|
|
+ val trackColor = Color.argb(
|
|
|
+ SWITCH_COMPAT_TRACK_ALPHA,
|
|
|
+ Color.red(scheme.primary),
|
|
|
+ Color.green(scheme.primary),
|
|
|
+ Color.blue(scheme.primary)
|
|
|
+ )
|
|
|
+
|
|
|
switchCompat.thumbTintList = ColorStateList(
|
|
|
arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
|
|
|
- intArrayOf(color, thumbUncheckedColor)
|
|
|
+ intArrayOf(scheme.primary, thumbUncheckedColor)
|
|
|
)
|
|
|
|
|
|
switchCompat.trackTintList = ColorStateList(
|
|
@@ -218,58 +564,49 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
}
|
|
|
|
|
|
fun colorDrawable(context: Context, drawable: Drawable) {
|
|
|
- val color = getElementColor(context)
|
|
|
- drawable.setTint(color)
|
|
|
+ val scheme = getScheme(context)
|
|
|
+ drawable.setTint(scheme.primary)
|
|
|
}
|
|
|
|
|
|
fun themeCheckbox(checkbox: CheckBox) {
|
|
|
- withElementColor(checkbox) { color ->
|
|
|
+ withScheme(checkbox) { scheme ->
|
|
|
checkbox.buttonTintList = ColorStateList(
|
|
|
arrayOf(
|
|
|
intArrayOf(-android.R.attr.state_checked),
|
|
|
intArrayOf(android.R.attr.state_checked)
|
|
|
),
|
|
|
- intArrayOf(Color.GRAY, color)
|
|
|
+ intArrayOf(Color.GRAY, scheme.primary)
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fun themeRadioButton(radioButton: RadioButton) {
|
|
|
- withElementColor(radioButton) { color ->
|
|
|
+ withScheme(radioButton) { scheme ->
|
|
|
radioButton.buttonTintList = ColorStateList(
|
|
|
arrayOf(
|
|
|
intArrayOf(-android.R.attr.state_checked),
|
|
|
intArrayOf(android.R.attr.state_checked)
|
|
|
),
|
|
|
- intArrayOf(Color.GRAY, color)
|
|
|
+ intArrayOf(Color.GRAY, scheme.primary)
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fun themeSwipeRefreshLayout(swipeRefreshLayout: SwipeRefreshLayout) {
|
|
|
- withElementColor(swipeRefreshLayout) { color ->
|
|
|
- swipeRefreshLayout.setColorSchemeColors(color)
|
|
|
+ withScheme(swipeRefreshLayout) { scheme ->
|
|
|
+ swipeRefreshLayout.setColorSchemeColors(scheme.primary)
|
|
|
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fun colorProgressBar(progressIndicator: LinearProgressIndicator) {
|
|
|
- withElementColor(progressIndicator) { color ->
|
|
|
- progressIndicator.setIndicatorColor(progressColor(progressIndicator.context, color))
|
|
|
+ withScheme(progressIndicator) { scheme ->
|
|
|
+ progressIndicator.setIndicatorColor(scheme.primary)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private fun progressColor(context: Context, color: Int): Int {
|
|
|
- val lightness = when (isDarkMode(context)) {
|
|
|
- true -> PROGRESS_LIGHTNESS_DARK_THEME
|
|
|
- false -> PROGRESS_LIGHTNESS_LIGHT_THEME
|
|
|
- }
|
|
|
- return colorUtil.setLightness(color, lightness)
|
|
|
- }
|
|
|
-
|
|
|
fun colorEditText(editText: EditText) {
|
|
|
- withElementColor(editText) { color ->
|
|
|
- editText.setTextColor(color)
|
|
|
+ withScheme(editText) { scheme ->
|
|
|
// TODO check API-level compatibility
|
|
|
// editText.background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
|
|
editText.backgroundTintList = ColorStateList(
|
|
@@ -278,16 +615,18 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
intArrayOf(android.R.attr.state_focused)
|
|
|
),
|
|
|
intArrayOf(
|
|
|
- Color.GRAY,
|
|
|
- color
|
|
|
+ scheme.outline,
|
|
|
+ scheme.primary
|
|
|
)
|
|
|
)
|
|
|
+ editText.setHintTextColor(scheme.onSurfaceVariant)
|
|
|
+ editText.setTextColor(scheme.onSurface)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fun colorTextInputLayout(textInputLayout: TextInputLayout) {
|
|
|
- withElementColor(textInputLayout) { color ->
|
|
|
- val errorColor = Color.GRAY
|
|
|
+ withScheme(textInputLayout) { scheme ->
|
|
|
+ val errorColor = scheme.onSurfaceVariant
|
|
|
|
|
|
val errorColorStateList = ColorStateList(
|
|
|
arrayOf(
|
|
@@ -305,8 +644,8 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
intArrayOf(android.R.attr.state_focused)
|
|
|
),
|
|
|
intArrayOf(
|
|
|
- Color.GRAY,
|
|
|
- color
|
|
|
+ scheme.outline,
|
|
|
+ scheme.primary
|
|
|
)
|
|
|
)
|
|
|
|
|
@@ -318,12 +657,32 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun colorTabLayout(tabLayout: TabLayout) {
|
|
|
- withElementColor(tabLayout) { color ->
|
|
|
- tabLayout.setSelectedTabIndicatorColor(color)
|
|
|
+ fun themeTabLayoutOnSurface(tabLayout: TabLayout) {
|
|
|
+ withScheme(tabLayout) { scheme ->
|
|
|
+ tabLayout.setBackgroundColor(scheme.surface)
|
|
|
+ colorTabLayout(tabLayout, scheme)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ fun colorTabLayout(tabLayout: TabLayout, scheme: Scheme) {
|
|
|
+ tabLayout.setSelectedTabIndicatorColor(scheme.primary)
|
|
|
+ tabLayout.tabTextColors = ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_selected),
|
|
|
+ intArrayOf(-android.R.attr.state_selected)
|
|
|
+ ),
|
|
|
+ intArrayOf(scheme.primary, ContextCompat.getColor(tabLayout.context, R.color.high_emphasis_text))
|
|
|
+ )
|
|
|
+ tabLayout.tabRippleColor = ColorStateList(
|
|
|
+ arrayOf(
|
|
|
+ intArrayOf(android.R.attr.state_pressed)
|
|
|
+ ),
|
|
|
+ intArrayOf(
|
|
|
+ colorUtil.adjustOpacity(scheme.primary, SURFACE_OPACITY_BUTTON_DISABLED)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
fun getPlaceholderImage(context: Context, mimetype: String?): Drawable? {
|
|
|
val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimetype)
|
|
|
val drawable = AppCompatResources.getDrawable(
|
|
@@ -337,28 +696,149 @@ class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private
|
|
|
}
|
|
|
|
|
|
fun colorChipBackground(chip: Chip) {
|
|
|
- withElementColor(chip) { color ->
|
|
|
- chip.chipBackgroundColor = ColorStateList.valueOf(color)
|
|
|
- chip.setTextColor(theme.colorText)
|
|
|
+ withScheme(chip) { scheme ->
|
|
|
+ chip.chipBackgroundColor = ColorStateList.valueOf(scheme.primary)
|
|
|
+ chip.setTextColor(scheme.onPrimary)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fun colorChipOutlined(chip: Chip, strokeWidth: Float) {
|
|
|
- withElementColor(chip) { color ->
|
|
|
+ withScheme(chip) { scheme ->
|
|
|
chip.chipBackgroundColor = ColorStateList.valueOf(Color.TRANSPARENT)
|
|
|
chip.chipStrokeWidth = strokeWidth
|
|
|
- chip.chipStrokeColor = ColorStateList.valueOf(color)
|
|
|
- chip.setTextColor(color)
|
|
|
+ chip.chipStrokeColor = ColorStateList.valueOf(scheme.primary)
|
|
|
+ chip.setTextColor(scheme.primary)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @TargetApi(Build.VERSION_CODES.O)
|
|
|
+ fun themePlaceholderAvatar(avatar: View, @DrawableRes foreground: Int): Drawable? {
|
|
|
+ var drawable: LayerDrawable? = null
|
|
|
+ withScheme(avatar) { scheme ->
|
|
|
+ val layers = arrayOfNulls<Drawable>(2)
|
|
|
+ layers[0] = ContextCompat.getDrawable(avatar.context, R.drawable.ic_avatar_background)
|
|
|
+ layers[0]?.setTint(scheme.surfaceVariant)
|
|
|
+ layers[1] = ContextCompat.getDrawable(avatar.context, foreground)
|
|
|
+ layers[1]?.setTint(scheme.onSurfaceVariant)
|
|
|
+ drawable = LayerDrawable(layers)
|
|
|
+ }
|
|
|
+
|
|
|
+ return drawable
|
|
|
+ }
|
|
|
+
|
|
|
+ fun themePrimaryMentionChip(context: Context, chip: ChipDrawable) {
|
|
|
+ withScheme(context) { scheme ->
|
|
|
+ chip.chipBackgroundColor = ColorStateList.valueOf(scheme.primary)
|
|
|
+ chip.setTextColor(scheme.onPrimary)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun setCheckedBackground(emoji: EmojiTextView) {
|
|
|
+ withScheme(emoji) { scheme ->
|
|
|
+ val drawable = AppCompatResources
|
|
|
+ .getDrawable(emoji.context, R.drawable.reaction_self_bottom_sheet_background)!!
|
|
|
+ .mutate()
|
|
|
+ DrawableCompat.setTintList(
|
|
|
+ drawable,
|
|
|
+ ColorStateList.valueOf(scheme.primary)
|
|
|
+ )
|
|
|
+ emoji.background = drawable
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun setCheckedBackground(linearLayout: LinearLayout, @ColorInt backgroundColor: Int) {
|
|
|
+ withScheme(linearLayout) { scheme ->
|
|
|
+ val drawable = AppCompatResources
|
|
|
+ .getDrawable(linearLayout.context, R.drawable.reaction_self_background)!!
|
|
|
+ .mutate()
|
|
|
+ DrawableCompat.setTintList(
|
|
|
+ drawable,
|
|
|
+ ColorStateList.valueOf(backgroundColor)
|
|
|
+ )
|
|
|
+ linearLayout.background = drawable
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorDialogMenuText(button: MaterialButton) {
|
|
|
+ withScheme(button) { scheme ->
|
|
|
+ button.setTextColor(scheme.onSurface)
|
|
|
+ button.iconTint = ColorStateList.valueOf(scheme.onSurface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorMaterialAlertDialogBackground(context: Context, dialogBuilder: MaterialAlertDialogBuilder) {
|
|
|
+ withScheme(dialogBuilder.context) { scheme ->
|
|
|
+ val materialShapeDrawable = MaterialShapeDrawable(
|
|
|
+ context,
|
|
|
+ null,
|
|
|
+ com.google.android.material.R.attr.alertDialogStyle,
|
|
|
+ com.google.android.material.R.style.MaterialAlertDialog_MaterialComponents
|
|
|
+ )
|
|
|
+ materialShapeDrawable.initializeElevationOverlay(context)
|
|
|
+ materialShapeDrawable.fillColor = ColorStateList.valueOf(scheme.surface)
|
|
|
+
|
|
|
+ // dialogCornerRadius first appeared in Android Pie
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
|
+ val radius = context.resources.getDimension(R.dimen.dialogBorderRadius)
|
|
|
+ materialShapeDrawable.setCornerSize(radius)
|
|
|
+ }
|
|
|
+
|
|
|
+ dialogBuilder.background = materialShapeDrawable
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorDialogHeadline(textView: TextView) {
|
|
|
+ withScheme(textView) { scheme ->
|
|
|
+ textView.setTextColor(scheme.onSurface)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorDialogSupportingText(textView: TextView) {
|
|
|
+ withScheme(textView) { scheme ->
|
|
|
+ textView.setTextColor(scheme.onSurfaceVariant)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorDialogIcon(icon: ImageView) {
|
|
|
+ withScheme(icon) { scheme ->
|
|
|
+ icon.setColorFilter(scheme.secondary)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ fun highlightText(textView: TextView, originalText: String, constraint: String) {
|
|
|
+ withScheme(textView) { scheme ->
|
|
|
+ FlexibleUtils.highlightText(textView, originalText, constraint, scheme.primary)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun createHighlightedSpan(context: Context, messageSpannable: SpannableString, searchTerm: String): Spannable {
|
|
|
+ var spannable: Spannable = messageSpannable
|
|
|
+ withScheme(context) { scheme ->
|
|
|
+ spannable = DisplayUtils.searchAndColor(messageSpannable, searchTerm, scheme.primary)
|
|
|
+ }
|
|
|
+ return spannable
|
|
|
+ }
|
|
|
+
|
|
|
+ fun colorMaterialAlertDialogIcon(context: Context, drawableId: Int): Drawable {
|
|
|
+ val drawable = AppCompatResources.getDrawable(context, drawableId)!!
|
|
|
+ withScheme(context) { scheme ->
|
|
|
+ DrawableCompat.setTint(drawable, scheme.secondary)
|
|
|
+ }
|
|
|
+ return drawable
|
|
|
+ }
|
|
|
+
|
|
|
companion object {
|
|
|
private val THEMEABLE_PLACEHOLDER_IDS = listOf(
|
|
|
R.drawable.ic_mimetype_package_x_generic,
|
|
|
R.drawable.ic_mimetype_folder
|
|
|
)
|
|
|
- private const val SWITCHCOMPAT_TRACK_ALPHA: Int = 77
|
|
|
- private const val PROGRESS_LIGHTNESS_LIGHT_THEME: Float = 0.76f
|
|
|
- private const val PROGRESS_LIGHTNESS_DARK_THEME: Float = 0.28f
|
|
|
+
|
|
|
+ private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt()
|
|
|
+
|
|
|
+ private const val SWITCH_COMPAT_TRACK_ALPHA: Int = 77
|
|
|
+ private const val HALF_ALPHA_INT: Int = 255 / 2
|
|
|
+ private const val SURFACE_OPACITY_BUTTON_DISABLED: Float = 0.12f
|
|
|
+ private const val ON_SURFACE_OPACITY_BUTTON_DISABLED: Float = 0.38f
|
|
|
+ private const val SEARCH_TEXT_SIZE: Float = 16f
|
|
|
}
|
|
|
}
|