ViewThemeUtils: split talk-specific and appcompat utils

Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
This commit is contained in:
Álvaro Brey 2022-08-29 17:57:54 +02:00
parent 1f32b35117
commit 892cc82ade
No known key found for this signature in database
GPG Key ID: 2585783189A62105
24 changed files with 369 additions and 278 deletions

View File

@ -206,7 +206,7 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(holder.binding.avatarDraweeView,
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
roundPlaceholderDrawable)));
} else {
holder.binding.avatarDraweeView.setImageResource(fallbackImageResource);

View File

@ -266,7 +266,7 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
holder.binding.dialogAvatar.setImageDrawable(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(holder.binding.dialogAvatar,
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
R.drawable.ic_avatar_document)));
} else {
holder.binding.dialogAvatar.setImageDrawable(
@ -316,7 +316,7 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
holder.binding.dialogAvatar.setImageDrawable(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(holder.binding.dialogAvatar,
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
R.drawable.ic_avatar_group)));
} else {
holder.binding.dialogAvatar.setImageDrawable(
@ -327,7 +327,7 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
holder.binding.dialogAvatar.setImageDrawable(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(holder.binding.dialogAvatar,
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
R.drawable.ic_avatar_link)));
} else {
holder.binding.dialogAvatar.setImageDrawable(

View File

@ -153,7 +153,7 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem<ParticipantIte
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(holder.binding.avatarDraweeView,
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
R.drawable.ic_avatar_group)));
} else {
holder.binding.avatarDraweeView.setImageResource(R.drawable.ic_circular_group);

View File

@ -155,7 +155,7 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(holder.binding.avatarDraweeView,
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
R.drawable.ic_avatar_group)));
} else {
holder.binding.avatarDraweeView.setImageResource(R.drawable.ic_circular_group);
@ -164,7 +164,7 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
holder.binding.avatarDraweeView.getHierarchy().setPlaceholderImage(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(holder.binding.avatarDraweeView,
viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.avatarDraweeView,
R.drawable.ic_avatar_mail)));
} else {
holder.binding.avatarDraweeView.setImageResource(R.drawable.ic_circular_mail);

View File

@ -161,7 +161,7 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : Mess
}
private fun colorizeMessageBubble(message: ChatMessage) {
viewThemeUtils.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
private fun setParentMessageDataOnMessageItem(message: ChatMessage) {

View File

@ -191,7 +191,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH
}
private fun colorizeMessageBubble(message: ChatMessage) {
viewThemeUtils.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
private fun setParentMessageDataOnMessageItem(message: ChatMessage) {

View File

@ -266,7 +266,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
}
private fun colorizeMessageBubble(message: ChatMessage) {
viewThemeUtils.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
private fun setParentMessageDataOnMessageItem(message: ChatMessage) {

View File

@ -87,7 +87,7 @@ class MagicIncomingTextMessageViewHolder(itemView: View, payload: Any) : Message
binding.messageAuthor.visibility = View.GONE
}
viewThemeUtils.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeIncomingMessageBubble(bubble, message.isGrouped, message.isDeleted)
itemView.isSelected = false

View File

@ -156,7 +156,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
}
private fun setBubbleOnChatMessage(message: ChatMessage) {
viewThemeUtils.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
private fun processMessageParameters(

View File

@ -167,8 +167,8 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
progressBar = getPreviewContactProgressBar();
getMessageText().setVisibility(View.INVISIBLE);
clickView = getPreviewContactContainer();
viewThemeUtils.colorContactChatItemBackground(getPreviewContactContainer());
viewThemeUtils.colorContactChatItemName(getPreviewContactName());
viewThemeUtils.talk.colorContactChatItemBackground(getPreviewContactContainer());
viewThemeUtils.talk.colorContactChatItemName(getPreviewContactName());
viewThemeUtils.platform.colorCircularProgressBarOnPrimaryContainer(getPreviewContactProgressBar());
} else {
getPreviewContainer().setVisibility(View.VISIBLE);

View File

@ -212,9 +212,9 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text
viewThemeUtils.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else {
@ -223,7 +223,7 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
}
private fun colorizeMessageBubble(message: ChatMessage) {
viewThemeUtils.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
private fun openGeoLink() {

View File

@ -176,9 +176,9 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text
viewThemeUtils.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else {
@ -187,7 +187,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
}
private fun colorizeMessageBubble(message: ChatMessage) {
viewThemeUtils.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
fun assignReactionInterface(reactionsInterface: ReactionsInterface) {

View File

@ -263,9 +263,9 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text
viewThemeUtils.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else {
@ -274,7 +274,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
}
private fun colorizeMessageBubble(message: ChatMessage) {
viewThemeUtils.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
}
fun assignVoiceMessageInterface(voiceMessageInterface: VoiceMessageInterface) {

View File

@ -112,7 +112,7 @@ class Reaction {
} else {
layoutInfo.viewThemeUtils.getScheme(emojiWithAmountWrapper.context).primaryContainer
}
layoutInfo.viewThemeUtils.setCheckedBackground(emojiWithAmountWrapper, color)
layoutInfo.viewThemeUtils.talk.setCheckedBackground(emojiWithAmountWrapper, color)
emojiWithAmountWrapper.setPaddingRelative(
layoutInfo.paddingSide,

View File

@ -334,7 +334,7 @@ class ContactsController(args: Bundle) :
val searchManager: SearchManager? = activity?.getSystemService(Context.SEARCH_SERVICE) as SearchManager?
if (searchItem != null) {
searchView = MenuItemCompat.getActionView(searchItem) as SearchView
viewThemeUtils.themeSearchView(searchView!!)
viewThemeUtils.appcompat.themeSearchView(searchView!!)
searchView!!.maxWidth = Int.MAX_VALUE
searchView!!.inputType = InputType.TYPE_TEXT_VARIATION_FILTER
var imeOptions: Int = EditorInfo.IME_ACTION_DONE or EditorInfo.IME_FLAG_NO_FULLSCREEN

View File

@ -780,7 +780,7 @@ class ConversationInfoController(args: Bundle) :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
binding.avatarImage.hierarchy.setPlaceholderImage(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(binding.avatarImage, R.drawable.ic_avatar_group)
viewThemeUtils.talk.themePlaceholderAvatar(binding.avatarImage, R.drawable.ic_avatar_group)
)
)
} else {
@ -793,7 +793,7 @@ class ConversationInfoController(args: Bundle) :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
binding.avatarImage.hierarchy.setPlaceholderImage(
DisplayUtils.getRoundedDrawable(
viewThemeUtils.themePlaceholderAvatar(binding.avatarImage, R.drawable.ic_avatar_link)
viewThemeUtils.talk.themePlaceholderAvatar(binding.avatarImage, R.drawable.ic_avatar_link)
)
)
} else {

View File

@ -296,7 +296,7 @@ class ConversationsListController(bundle: Bundle) :
val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager?
if (searchItem != null) {
searchView = MenuItemCompat.getActionView(searchItem) as SearchView
viewThemeUtils.themeSearchView(searchView!!)
viewThemeUtils.appcompat.themeSearchView(searchView!!)
searchView!!.maxWidth = Int.MAX_VALUE
searchView!!.inputType = InputType.TYPE_TEXT_VARIATION_FILTER
var imeOptions = EditorInfo.IME_ACTION_DONE or EditorInfo.IME_FLAG_NO_FULLSCREEN

View File

@ -98,7 +98,7 @@ class RemoteFileBrowserItemsListViewHolder(
binding.fileIcon
.hierarchy
.setPlaceholderImage(
viewThemeUtils.getPlaceholderImage(binding.root.context, item.mimeType)
viewThemeUtils.talk.getPlaceholderImage(binding.root.context, item.mimeType)
)
if (item.hasPreview) {

View File

@ -70,7 +70,7 @@ abstract class SharedItemsViewHolder(
)
open fun onBind(item: SharedFileItem) {
image.hierarchy.setPlaceholderImage(viewThemeUtils.getPlaceholderImage(image.context, item.mimeType))
image.hierarchy.setPlaceholderImage(viewThemeUtils.talk.getPlaceholderImage(image.context, item.mimeType))
if (item.previewAvailable) {
image.controller = configurePreview(item)
}

View File

@ -225,7 +225,7 @@ class MessageActionsDialog(
private fun checkAndSetEmojiSelfReaction(emoji: EmojiTextView) {
if (emoji.text?.toString() != null && message.reactionsSelf?.contains(emoji.text?.toString()) == true) {
viewThemeUtils.setCheckedBackground(emoji)
viewThemeUtils.talk.setCheckedBackground(emoji)
}
}

View File

@ -21,48 +21,27 @@
package com.nextcloud.talk.ui.theme
import android.annotation.TargetApi
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
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.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
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.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.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.ChipDrawable
import com.nextcloud.android.common.ui.theme.MaterialSchemes
import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase
import com.nextcloud.talk.R
import com.nextcloud.talk.ui.theme.viewthemeutils.AndroidViewThemeUtils
import com.nextcloud.talk.ui.theme.viewthemeutils.AppCompatViewThemeUtils
import com.nextcloud.talk.ui.theme.viewthemeutils.MaterialViewThemeUtils
import com.nextcloud.talk.ui.theme.viewthemeutils.TalkSpecificViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.DrawableUtils
import com.vanniktech.emoji.EmojiTextView
import com.yarolegovich.mp.MaterialPreferenceCategory
import com.yarolegovich.mp.MaterialSwitchPreference
import eu.davidea.flexibleadapter.utils.FlexibleUtils
import javax.inject.Inject
import kotlin.math.roundToInt
@Suppress("TooManyFunctions")
class ViewThemeUtils @Inject constructor(
@ -70,149 +49,13 @@ class ViewThemeUtils @Inject constructor(
@JvmField
val platform: AndroidViewThemeUtils,
@JvmField
val material: MaterialViewThemeUtils
val material: MaterialViewThemeUtils,
@JvmField
val appcompat: AppCompatViewThemeUtils,
@JvmField
val talk: TalkSpecificViewThemeUtils
) : ViewThemeUtilsBase(schemes) {
fun themeSearchView(searchView: SearchView) {
withScheme(searchView) { scheme ->
// hacky as no default way is provided
val editText = searchView.findViewById<SearchView.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 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 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 colorContactChatItemName(contactName: androidx.emoji.widget.EmojiTextView) {
withScheme(contactName) { scheme ->
contactName.setTextColor(scheme.onPrimaryContainer)
}
}
fun colorContactChatItemBackground(card: MaterialCardView) {
withScheme(card) { scheme ->
card.setCardBackgroundColor(scheme.primaryContainer)
}
}
// TODO split this util into classes depending on framework views vs library views
fun colorPreferenceCategory(category: MaterialPreferenceCategory) {
withScheme(category) { scheme ->
category.setTitleColor(scheme.primary)
}
}
fun colorSwitchPreference(preference: MaterialSwitchPreference) {
val children = preference.children
val switch = children.find { it is SwitchCompat }
if (switch != null) {
val switchCompat = (switch as SwitchCompat)
colorSwitchCompat(switchCompat)
}
}
fun colorSwitchCompat(switchCompat: SwitchCompat) {
withScheme(switchCompat) { scheme ->
val context = switchCompat.context
val thumbUncheckedColor = ResourcesCompat.getColor(
context.resources,
R.color.switch_thumb_color_unchecked,
context.theme
)
val trackUncheckedColor = ResourcesCompat.getColor(
context.resources,
R.color.switch_track_color_unchecked,
context.theme
)
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(scheme.primary, thumbUncheckedColor)
)
switchCompat.trackTintList = ColorStateList(
arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
intArrayOf(trackColor, trackUncheckedColor)
)
}
}
fun themeSwipeRefreshLayout(swipeRefreshLayout: SwipeRefreshLayout) {
withScheme(swipeRefreshLayout) { scheme ->
swipeRefreshLayout.setColorSchemeColors(scheme.primary)
@ -220,71 +63,6 @@ class ViewThemeUtils @Inject constructor(
}
}
fun getPlaceholderImage(context: Context, mimetype: String?): Drawable? {
val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimetype)
val drawable = AppCompatResources.getDrawable(
context,
drawableResourceId
)
if (drawable != null && THEMEABLE_PLACEHOLDER_IDS.contains(drawableResourceId)) {
colorDrawable(context, drawable)
}
return drawable
}
private fun colorDrawable(context: Context, drawable: Drawable) {
val scheme = getScheme(context)
drawable.setTint(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)
@ -331,18 +109,4 @@ class ViewThemeUtils @Inject constructor(
}
return drawable
}
companion object {
private val THEMEABLE_PLACEHOLDER_IDS = listOf(
R.drawable.ic_mimetype_package_x_generic,
R.drawable.ic_mimetype_folder
)
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 SEARCH_TEXT_SIZE: Float = 16f
}
}

View File

@ -0,0 +1,93 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme.viewthemeutils
import android.content.res.ColorStateList
import android.graphics.Color
import android.widget.LinearLayout
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.SwitchCompat
import androidx.core.content.res.ResourcesCompat
import com.nextcloud.android.common.ui.color.ColorUtil
import com.nextcloud.android.common.ui.theme.MaterialSchemes
import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase
import com.nextcloud.talk.R
import javax.inject.Inject
/**
* View theme utils for AppCompat views (androidx.appcompat.*)
*/
class AppCompatViewThemeUtils @Inject constructor(schemes: MaterialSchemes, private val colorUtil: ColorUtil) :
ViewThemeUtilsBase(schemes) {
fun themeSearchView(searchView: SearchView) {
withScheme(searchView) { scheme ->
// hacky as no default way is provided
val editText = searchView.findViewById<SearchView.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 colorSwitchCompat(switchCompat: SwitchCompat) {
withScheme(switchCompat) { scheme ->
val context = switchCompat.context
val thumbUncheckedColor = ResourcesCompat.getColor(
context.resources,
R.color.switch_thumb_color_unchecked,
context.theme
)
val trackUncheckedColor = ResourcesCompat.getColor(
context.resources,
R.color.switch_track_color_unchecked,
context.theme
)
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(scheme.primary, thumbUncheckedColor)
)
switchCompat.trackTintList = ColorStateList(
arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
intArrayOf(trackColor, trackUncheckedColor)
)
}
}
companion object {
private const val SWITCH_COMPAT_TRACK_ALPHA: Int = 77
private const val SEARCH_TEXT_SIZE: Float = 16f
}
}

View File

@ -0,0 +1,234 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme.viewthemeutils
import android.annotation.TargetApi
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.os.Build
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
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 com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.ChipDrawable
import com.nextcloud.android.common.ui.theme.MaterialSchemes
import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase
import com.nextcloud.talk.R
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.DrawableUtils
import com.vanniktech.emoji.EmojiTextView
import com.yarolegovich.mp.MaterialPreferenceCategory
import com.yarolegovich.mp.MaterialSwitchPreference
import javax.inject.Inject
import kotlin.math.roundToInt
/**
* View theme utils specific for the Talk app.
*
* TODO some of these can be renamed and made generic
*/
@Suppress("TooManyFunctions")
class TalkSpecificViewThemeUtils @Inject constructor(
schemes: MaterialSchemes,
private val appcompat: AppCompatViewThemeUtils
) :
ViewThemeUtilsBase(schemes) {
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 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 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 colorPreferenceCategory(category: MaterialPreferenceCategory) {
withScheme(category) { scheme ->
category.setTitleColor(scheme.primary)
}
}
fun colorSwitchPreference(preference: MaterialSwitchPreference) {
val children = preference.children
val switch = children.find { it is SwitchCompat }
if (switch != null) {
val switchCompat = (switch as SwitchCompat)
appcompat.colorSwitchCompat(switchCompat)
}
}
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 getPlaceholderImage(context: Context, mimetype: String?): Drawable? {
val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimetype)
val drawable = AppCompatResources.getDrawable(
context,
drawableResourceId
)
if (drawable != null && THEMEABLE_PLACEHOLDER_IDS.contains(drawableResourceId)) {
colorDrawable(context, drawable)
}
return drawable
}
private fun colorDrawable(context: Context, drawable: Drawable) {
val scheme = getScheme(context)
drawable.setTint(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)
}
}
companion object {
private val THEMEABLE_PLACEHOLDER_IDS = listOf(
R.drawable.ic_mimetype_package_x_generic,
R.drawable.ic_mimetype_folder
)
private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt()
private const val HALF_ALPHA_INT: Int = 255 / 2
}
}

View File

@ -304,7 +304,7 @@ public class DisplayUtils {
chip.setEllipsize(TextUtils.TruncateAt.MIDDLE);
if (chipResource == R.xml.chip_you) {
viewThemeUtils.themePrimaryMentionChip(context, chip);
viewThemeUtils.talk.themePrimaryMentionChip(context, chip);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {