improved color scheme & theming (#4507)

Follow up to https://github.com/tuskyapp/Tusky/pull/3921

- no more hardcoded `tusky_blue`, instead the `colorPrimary` attribute
is used. This will help us when adding more themes, e.g a dynamic color
one.
- The `colorPrimary` of the dark theme is now lighter for more contrast
and subsequently the `colorOnPrimary` is now dark grey instead of white.
- `tusky_red_lighter` is now a bit more red than before
- Tweaked color usage in a few places for better contrast

I think this looks a bit unfamiliar but overall better and the higher
contrast makes things noticeably easier to read.

<img
src="https://github.com/tuskyapp/Tusky/assets/10157047/4cbb92d8-b772-4e94-bc15-c4baf0e5473f"
width="260"/>
This commit is contained in:
Konrad Pozniak 2024-06-19 16:50:35 +02:00 committed by GitHub
commit dcbd6d558c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 272 additions and 233 deletions

View file

@ -84,7 +84,6 @@ class ListsActivity : BaseActivity() {
)
binding.swipeRefreshLayout.setOnRefreshListener { viewModel.retryLoading() }
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
lifecycleScope.launch {
viewModel.state.collect(this@ListsActivity::update)

View file

@ -58,6 +58,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.FixedSizeDrawable
import com.bumptech.glide.request.transition.Transition
import com.google.android.material.R as materialR
import com.google.android.material.color.MaterialColors
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
@ -698,8 +699,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
startActivityWithSlideInAnimation(AnnouncementsActivity.newIntent(context))
}
badgeStyle = BadgeStyle().apply {
textColor = ColorHolder.fromColor(MaterialColors.getColor(binding.mainDrawer, com.google.android.material.R.attr.colorOnPrimary))
color = ColorHolder.fromColor(MaterialColors.getColor(binding.mainDrawer, com.google.android.material.R.attr.colorPrimary))
textColor = ColorHolder.fromColor(MaterialColors.getColor(binding.mainDrawer, materialR.attr.colorOnPrimary))
color = ColorHolder.fromColor(MaterialColors.getColor(binding.mainDrawer, materialR.attr.colorPrimary))
}
},
DividerDrawerItem(),
@ -856,7 +857,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
if (tabs[position].id == DIRECT) {
val badge = tab.orCreateBadge
badge.isVisible = accountManager.activeAccount?.hasDirectMessageBadge ?: false
badge.backgroundColor = MaterialColors.getColor(binding.mainDrawer, com.google.android.material.R.attr.colorPrimary)
badge.backgroundColor = MaterialColors.getColor(binding.mainDrawer, materialR.attr.colorPrimary)
directMessageTab = tab
}
}.also { it.attach() }

View file

@ -49,6 +49,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.viewpager2.widget.MarginPageTransformer
import com.bumptech.glide.Glide
import com.google.android.material.R as materialR
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.chip.Chip
import com.google.android.material.color.MaterialColors
@ -189,9 +190,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
* Load colors and dimensions from resources
*/
private fun loadResources() {
toolbarColor = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurface, Color.BLACK)
toolbarColor = MaterialColors.getColor(this, materialR.attr.colorSurface, Color.BLACK)
statusBarColorTransparent = getColor(R.color.transparent_statusbar_background)
statusBarColorOpaque = MaterialColors.getColor(this, androidx.appcompat.R.attr.colorPrimaryDark, Color.BLACK)
statusBarColorOpaque = MaterialColors.getColor(this, materialR.attr.colorPrimaryDark, Color.BLACK)
avatarSize = resources.getDimension(R.dimen.account_activity_avatar_size)
titleVisibleHeight = resources.getDimensionPixelSize(R.dimen.account_activity_scroll_title_visible_height)
}
@ -458,7 +459,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
binding.swipeToRefreshLayout.isRefreshing = isRefreshing == true
}
}
binding.swipeToRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
}
private fun onAccountChanged(account: Account?) {

View file

@ -104,7 +104,6 @@ class AccountMediaFragment :
binding.swipeRefreshLayout.isEnabled = false
binding.swipeRefreshLayout.setOnRefreshListener { refreshContent() }
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.statusView.visibility = View.GONE

View file

@ -11,6 +11,7 @@ import androidx.core.view.setPadding
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import com.bumptech.glide.Glide
import com.google.android.material.R as materialR
import com.google.android.material.color.MaterialColors
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.databinding.ItemAccountMediaBinding
@ -47,7 +48,7 @@ class AccountMediaGridAdapter(
private val baseItemBackgroundColor = MaterialColors.getColor(
context,
com.google.android.material.R.attr.colorSurface,
materialR.attr.colorSurface,
Color.BLACK
)
private val videoIndicator = AppCompatResources.getDrawable(

View file

@ -138,7 +138,6 @@ class AccountListFragment :
binding.recyclerView.addOnScrollListener(scrollListener)
binding.swipeRefreshLayout.setOnRefreshListener { fetchAccounts(adapter) }
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
fetchAccounts(adapter)
}

View file

@ -91,7 +91,6 @@ class AnnouncementsActivity :
}
binding.swipeRefreshLayout.setOnRefreshListener(this::refreshAnnouncements)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.announcementsList.setHasFixedSize(true)
binding.announcementsList.layoutManager = LinearLayoutManager(this)

View file

@ -43,6 +43,7 @@ import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
@ -61,6 +62,7 @@ import androidx.transition.TransitionManager
import com.canhub.cropper.CropImage
import com.canhub.cropper.CropImageContract
import com.canhub.cropper.options
import com.google.android.material.R as materialR
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
import com.google.android.material.color.MaterialColors
@ -821,24 +823,26 @@ class ComposeActivity :
binding.descriptionMissingWarningButton.hide()
} else {
binding.composeHideMediaButton.show()
@ColorInt val color = if (contentWarningShown) {
@AttrRes val color = if (contentWarningShown) {
binding.composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp)
binding.composeHideMediaButton.isClickable = false
getColor(R.color.transparent_tusky_blue)
materialR.attr.colorPrimary
} else {
binding.composeHideMediaButton.isClickable = true
if (markMediaSensitive) {
binding.composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp)
getColor(R.color.tusky_blue)
materialR.attr.colorPrimary
} else {
binding.composeHideMediaButton.setImageResource(R.drawable.ic_eye_24dp)
MaterialColors.getColor(
binding.composeHideMediaButton,
android.R.attr.textColorTertiary
)
android.R.attr.textColorTertiary
}
}
binding.composeHideMediaButton.drawable.setTint(color)
binding.composeHideMediaButton.drawable.setTint(
MaterialColors.getColor(
binding.composeHideMediaButton,
color
)
)
var oneMediaWithoutDescription = false
for (media in viewModel.media.value) {
@ -856,14 +860,15 @@ class ComposeActivity :
// Can't reschedule a published status
enableButton(binding.composeScheduleButton, clickable = false, colorActive = false)
} else {
@ColorInt val color = if (binding.composeScheduleView.time == null) {
@ColorInt val color =
MaterialColors.getColor(
binding.composeScheduleButton,
android.R.attr.textColorTertiary
if (binding.composeScheduleView.time == null) {
android.R.attr.textColorTertiary
} else {
materialR.attr.colorPrimary
}
)
} else {
getColor(R.color.tusky_blue)
}
binding.composeScheduleButton.drawable.setTint(color)
}
}
@ -1242,22 +1247,24 @@ class ComposeActivity :
TransitionManager.beginDelayedTransition(
binding.composeContentWarningBar.parent as ViewGroup
)
@ColorInt val color = if (show) {
@AttrRes val color = if (show) {
binding.composeContentWarningBar.show()
binding.composeContentWarningField.setSelection(
binding.composeContentWarningField.text.length
)
binding.composeContentWarningField.requestFocus()
getColor(R.color.tusky_blue)
materialR.attr.colorPrimary
} else {
binding.composeContentWarningBar.hide()
binding.composeEditField.requestFocus()
MaterialColors.getColor(
binding.composeContentWarningButton,
android.R.attr.textColorTertiary
)
android.R.attr.textColorTertiary
}
binding.composeContentWarningButton.drawable.setTint(color)
binding.composeContentWarningButton.drawable.setTint(
MaterialColors.getColor(
binding.composeHideMediaButton,
color
)
)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {

View file

@ -24,6 +24,8 @@ import android.graphics.RectF
import android.util.AttributeSet
import androidx.appcompat.content.res.AppCompatResources
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.R as materialR
import com.google.android.material.color.MaterialColors
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.view.MediaPreviewImageView
@ -37,7 +39,7 @@ class ProgressImageView
private val progressRect = RectF()
private val biggerRect = RectF()
private val circlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = context.getColor(R.color.tusky_blue)
color = MaterialColors.getColor(this@ProgressImageView, materialR.attr.colorPrimary)
strokeWidth = Utils.dpToPx(context, 4).toFloat()
style = Paint.Style.STROKE
}
@ -46,13 +48,15 @@ class ProgressImageView
}
private val markBgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
color = context.getColor(R.color.tusky_grey_10)
color = MaterialColors.getColor(this@ProgressImageView, android.R.attr.colorBackground)
}
private val captionDrawable = AppCompatResources.getDrawable(
context,
R.drawable.spellcheck
)!!.apply {
setTint(Color.WHITE)
setTint(
MaterialColors.getColor(this@ProgressImageView, android.R.attr.textColorTertiary)
)
}
private val circleRadius = Utils.dpToPx(context, 14)
private val circleMargin = Utils.dpToPx(context, 14)
@ -68,8 +72,10 @@ class ProgressImageView
}
fun setChecked(checked: Boolean) {
markBgPaint.color =
context.getColor(if (checked) R.color.tusky_blue else R.color.tusky_grey_10)
val backgroundColor = if (checked) materialR.attr.colorPrimary else android.R.attr.colorBackground
val foregroundColor = if (checked) materialR.attr.colorOnPrimary else android.R.attr.textColorTertiary
markBgPaint.color = MaterialColors.getColor(this, backgroundColor)
captionDrawable.setTint(MaterialColors.getColor(this, foregroundColor))
invalidate()
}

View file

@ -109,7 +109,7 @@ class ConversationsFragment :
setupRecyclerView(adapter)
initSwipeToRefresh()
binding.swipeRefreshLayout.setOnRefreshListener { refreshContent() }
adapter.addLoadStateListener { loadState ->
if (loadState.refresh != LoadState.Loading && loadState.source.refresh != LoadState.Loading) {
@ -245,11 +245,6 @@ class ConversationsFragment :
adapter?.refresh()
}
private fun initSwipeToRefresh() {
binding.swipeRefreshLayout.setOnRefreshListener { refreshContent() }
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
}
override fun onReblog(reblog: Boolean, position: Int) {
// its impossible to reblog private messages
}

View file

@ -48,7 +48,6 @@ class FiltersActivity : BaseActivity(), FiltersListener {
}
binding.swipeRefreshLayout.setOnRefreshListener { reloadFilters() }
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
setTitle(R.string.pref_title_timeline_filters)

View file

@ -137,7 +137,6 @@ class NotificationsFragment :
// Setup the SwipeRefreshLayout.
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
// Setup the RecyclerView.
binding.recyclerView.setHasFixedSize(true)

View file

@ -102,6 +102,7 @@ class ReportStatusesFragment :
startActivity(intent)
}
}
Attachment.Type.UNKNOWN -> {
}
}
@ -111,7 +112,7 @@ class ReportStatusesFragment :
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
handleClicks()
initStatusesView()
setupSwipeRefreshLayout()
binding.swipeRefreshLayout.setOnRefreshListener(this)
}
override fun onDestroyView() {
@ -138,6 +139,7 @@ class ReportStatusesFragment :
onRefresh()
true
}
else -> false
}
}
@ -148,12 +150,6 @@ class ReportStatusesFragment :
adapter?.refresh()
}
private fun setupSwipeRefreshLayout() {
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.swipeRefreshLayout.setOnRefreshListener(this)
}
private fun initStatusesView() {
val statusDisplayOptions = StatusDisplayOptions(
animateAvatars = false,

View file

@ -77,7 +77,6 @@ class ScheduledStatusActivity :
}
binding.swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.scheduledTootList.setHasFixedSize(true)
binding.scheduledTootList.layoutManager = LinearLayoutManager(this)

View file

@ -62,7 +62,7 @@ abstract class SearchFragment<T : Any> :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val adapter = initAdapter()
setupSwipeRefreshLayout()
binding.swipeRefreshLayout.setOnRefreshListener(this)
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
subscribeObservables(adapter)
}
@ -74,11 +74,6 @@ abstract class SearchFragment<T : Any> :
super.onDestroyView()
}
private fun setupSwipeRefreshLayout() {
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
}
private fun subscribeObservables(adapter: PagingDataAdapter<T, *>) {
viewLifecycleOwner.lifecycleScope.launch {
data.collectLatest { pagingData ->
@ -131,6 +126,7 @@ abstract class SearchFragment<T : Any> :
onRefresh()
true
}
else -> false
}
}

View file

@ -373,7 +373,6 @@ class TimelineFragment :
private fun setupSwipeRefreshLayout() {
binding.swipeRefreshLayout.isEnabled = isSwipeToRefreshEnabled
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
}
private fun setupRecyclerView(adapter: TimelinePagingAdapter) {

View file

@ -72,7 +72,7 @@ class TrendingTagsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val adapter = TrendingTagsAdapter(::onViewTag)
this.adapter = adapter
setupSwipeRefreshLayout()
binding.swipeRefreshLayout.setOnRefreshListener(this)
setupRecyclerView(adapter)
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
@ -106,11 +106,6 @@ class TrendingTagsFragment :
super.onDestroyView()
}
private fun setupSwipeRefreshLayout() {
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
}
private fun setupLayoutManager(adapter: TrendingTagsAdapter, columnCount: Int) {
binding.recyclerView.layoutManager = GridLayoutManager(context, columnCount).apply {
spanSizeLookup = object : SpanSizeLookup() {

View file

@ -126,7 +126,6 @@ class ViewThreadFragment :
this.adapter = adapter
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(context)

View file

@ -75,7 +75,6 @@ class ViewEditsFragment :
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(context)

View file

@ -43,6 +43,7 @@ import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.net.toUri
import androidx.preference.PreferenceManager
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.R as materialR
import com.google.android.material.color.MaterialColors
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.HashTag
@ -329,7 +330,7 @@ private fun openLinkInBrowser(uri: Uri?, context: Context) {
fun openLinkInCustomTab(uri: Uri, context: Context) {
val toolbarColor = MaterialColors.getColor(
context,
com.google.android.material.R.attr.colorSurface,
materialR.attr.colorSurface,
Color.BLACK
)
val navigationbarColor = MaterialColors.getColor(

View file

@ -26,6 +26,8 @@ import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.Dimension
import androidx.core.content.res.use
import com.google.android.material.R as materialR
import com.google.android.material.color.MaterialColors
import com.keylesspalace.tusky.R
import kotlin.math.max
@ -127,37 +129,29 @@ class GraphView @JvmOverloads constructor(
private fun initFromXML(attr: AttributeSet?) {
context.obtainStyledAttributes(attr, R.styleable.GraphView).use { a ->
primaryLineColor = context.getColor(
a.getResourceId(
R.styleable.GraphView_primaryLineColor,
R.color.tusky_blue
)
primaryLineColor = a.getColor(
R.styleable.GraphView_primaryLineColor,
MaterialColors.getColor(this, materialR.attr.colorPrimary)
)
secondaryLineColor = context.getColor(
a.getResourceId(
R.styleable.GraphView_secondaryLineColor,
R.color.tusky_red
)
secondaryLineColor = a.getColor(
R.styleable.GraphView_secondaryLineColor,
context.getColor(R.color.warning_color)
)
lineWidth = a.getDimensionPixelSize(
lineWidth = a.getDimension(
R.styleable.GraphView_lineWidth,
R.dimen.graph_line_thickness
).toFloat()
graphColor = context.getColor(
a.getResourceId(
R.styleable.GraphView_graphColor,
R.color.colorBackground
)
context.resources.getDimension(R.dimen.graph_line_thickness)
)
metaColor = context.getColor(
a.getResourceId(
R.styleable.GraphView_metaColor,
R.color.dividerColor
)
graphColor = a.getColor(
R.styleable.GraphView_graphColor,
context.getColor(R.color.colorBackground)
)
metaColor = a.getColor(
R.styleable.GraphView_metaColor,
context.getColor(R.color.dividerColor)
)
proportionalTrending = a.getBoolean(

View file

@ -20,6 +20,7 @@ import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.core.content.res.use
import com.google.android.material.R as materialR
import com.google.android.material.card.MaterialCardView
import com.google.android.material.color.MaterialColors
import com.keylesspalace.tusky.R
@ -40,7 +41,7 @@ class LicenseCard
setCardBackgroundColor(
MaterialColors.getColor(
context,
com.google.android.material.R.attr.colorSurface,
materialR.attr.colorSurface,
Color.BLACK
)
)

View file

@ -0,0 +1,37 @@
/* Copyright 2024 Tusky contributors
*
* This file is a part of Tusky.
*
* 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.
*
* Tusky 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 Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.view
import android.content.Context
import android.util.AttributeSet
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.R as materialR
import com.google.android.material.color.MaterialColors
/**
* SwipeRefreshLayout does not allow theming of the color scheme,
* so we use this class to still have a single point to change its colors.
*/
class TuskySwipeRefreshLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {
init {
setColorSchemeColors(
MaterialColors.getColor(this, materialR.attr.colorPrimary)
)
}
}