Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -762,14 +762,14 @@ class ConfigurationFragment @JvmOverloads constructor(

2 -> {
profileStatusText = profile.error
profileStatusColor = context.getColour(R.color.material_red_500)
profileStatusColor = context.getColorAttr(R.attr.testFailColor)
}

3 -> {
val err = profile.error ?: ""
val msg = Protocols.genFriendlyMsg(err)
profileStatusText = if (msg != err) msg else getString(R.string.unavailable)
profileStatusColor = context.getColour(R.color.material_red_500)
profileStatusColor = context.getColorAttr(R.attr.testFailColor)
}
}

Expand Down Expand Up @@ -1803,6 +1803,7 @@ class ConfigurationFragment @JvmOverloads constructor(
}

profileName.text = proxyEntity.displayName()
profileName.setTextColor(requireContext().getColorAttr(R.attr.profileNameColor))
profileType.text = proxyEntity.displayType()
profileType.setTextColor(requireContext().getProtocolColor(proxyEntity.type))

Expand Down Expand Up @@ -1854,7 +1855,7 @@ class ConfigurationFragment @JvmOverloads constructor(
profileStatus.text = getString(R.string.available, proxyEntity.ping)
profileStatus.setTextColor(requireContext().getColour(R.color.material_green_500))
} else {
profileStatus.setTextColor(requireContext().getColour(R.color.material_red_500))
profileStatus.setTextColor(requireContext().getColorAttr(R.attr.testFailColor))
if (proxyEntity.status == 2) {
profileStatus.text = proxyEntity.error
}
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/java/io/nekohasekai/sagernet/widget/ServiceButton.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.nekohasekai.sagernet.widget

import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.AttributeSet
Expand All @@ -17,6 +18,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.BaseProgressIndicator
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.bg.BaseService
import io.nekohasekai.sagernet.ktx.getColorAttr
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import java.util.*
Expand Down Expand Up @@ -118,6 +120,7 @@ class ServiceButton @JvmOverloads constructor(
}
checked = state == BaseService.State.Connected
refreshDrawableState()
applyStateTint(state)
val description = context.getText(if (state.canStop) R.string.stop else R.string.connect)
contentDescription = description
TooltipCompat.setTooltipText(this, description)
Expand All @@ -129,6 +132,18 @@ class ServiceButton @JvmOverloads constructor(
)
}

private fun applyStateTint(state: BaseService.State) {
// Tint the connect FAB icon by state: connected=green, stopped=red,
// transient states neutral (cleared). Non-Dracula themes default these
// attrs to the primary text color, so there is no visible change.
val attr = when (state) {
BaseService.State.Connected -> R.attr.statusConnectedColor
BaseService.State.Stopped -> R.attr.statusStoppedColor
else -> null
}
imageTintList = attr?.let { ColorStateList.valueOf(context.getColorAttr(it)) }
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

private fun changeState(icon: AnimatedState, animate: Boolean) {
fun counters(a: AnimatedState, b: AnimatedState): Boolean =
a == iconStopped && b == iconConnecting ||
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/io/nekohasekai/sagernet/widget/StatsBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,21 @@ class StatsBar @JvmOverloads constructor(
TooltipCompat.setTooltipText(this, text)
}

private fun setStatusColorByState(state: BaseService.State) {
// Connected = statusConnectedColor, Stopped = statusStoppedColor, transient
// states stay neutral. For non-Dracula themes these attrs default to the
// primary text color, so there is no visible change.
val attr = when (state) {
BaseService.State.Connected -> R.attr.statusConnectedColor
BaseService.State.Stopped -> R.attr.statusStoppedColor
else -> android.R.attr.textColorPrimary
}
statusText.setTextColor(context.getColorAttr(attr))
}

fun changeState(state: BaseService.State) {
val activity = context as MainActivity
setStatusColorByState(state)
fun postWhenStarted(what: () -> Unit) = activity.lifecycleScope.launch(Dispatchers.Main) {
delay(100L)
activity.whenStarted { what() }
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/moe/matsuri/nb4a/Protocols.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ object Protocols {
fun Context.getProtocolColor(type: Int): Int {
return when (type) {
TYPE_NEKO -> getColorAttr(android.R.attr.textColorPrimary)
else -> getColorAttr(R.attr.accentOrTextSecondary)
else -> getColorAttr(R.attr.protocolColor)
}
}

Expand Down
8 changes: 8 additions & 0 deletions app/src/main/res/values-night/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
<item name="android:colorBackground">@color/dracula_background</item>
<item name="colorOnBackground">@color/dracula_on_surface</item>
<item name="android:windowBackground">@color/dracula_background</item>
<!-- Night is Dracula's active code path (it forces night mode on), so the
semantic color overrides must live here too, not only day. -->
<item name="protocolColor">@color/color_dracula_green</item>
<item name="profileNameColor">@color/color_dracula_yellow</item>
<item name="statusConnectedColor">@color/color_dracula_green</item>
<item name="statusStoppedColor">@color/color_dracula_red</item>
<item name="testFailColor">@color/color_dracula_red</item>
</style>

<style name="Theme.SagerNet.Dialog.Dracula" parent="Theme.SagerNet.Dialog">
Expand All @@ -40,5 +47,6 @@
<item name="colorOnSurface">@color/dracula_on_surface</item>
<item name="android:colorBackground">@color/dracula_background</item>
<item name="colorOnBackground">@color/dracula_on_surface</item>
<item name="protocolColor">@color/color_dracula_green</item>
</style>
Comment thread
greptile-apps[bot] marked this conversation as resolved.
</resources>
6 changes: 6 additions & 0 deletions app/src/main/res/values-v26/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
<!-- Default nav-drawer selected-item fill; satisfies the
?itemShapeFillColor self-reference in @color/navigation_item. -->
<item name="itemShapeFillColor">?colorPrimary</item>
<!-- Protocol/type label color; defaults to accent, Dracula overrides green. -->
<item name="protocolColor">?attr/accentOrTextSecondary</item>
<item name="profileNameColor">?android:attr/textColorPrimary</item>
<item name="statusConnectedColor">?android:attr/textColorPrimary</item>
<item name="statusStoppedColor">?android:attr/textColorPrimary</item>
<item name="testFailColor">@color/material_red_500</item>
<item name="actionModeCloseDrawable">@drawable/ic_navigation_close</item>
<item name="colorButtonNormal">?colorAccent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@
<attr name="fabColorBackground" format="color" />
<attr name="selectedColorPrimary" format="color" />
<attr name="whiteOrTextPrimary" format="color" />
<!-- Text color for the protocol/type label on a profile row. Defaults to
the accent color; Dracula overrides it with its green. -->
<attr name="protocolColor" format="color" />
<!-- Profile name label color. Defaults to primary text; Dracula = yellow. -->
<attr name="profileNameColor" format="color" />
<!-- Service status / connect-button colors. Default to primary text so non-
Dracula themes are unchanged; Dracula tints connected=green, stopped=red. -->
<attr name="statusConnectedColor" format="color" />
<attr name="statusStoppedColor" format="color" />
<!-- Connection-test failure color. Default Material red; Dracula = #ff5555. -->
<attr name="testFailColor" format="color" />
<!-- Tonal "elevated surface" color for cards (route / group); each theme
variant tints toward its own primary. -->
<attr name="cardElevatedSurfaceColor" format="color" />
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,12 @@
<color name="color_dracula_accent">#FF79C6</color>
<color name="color_dracula_100">#E9DDFB</color>
<color name="color_dracula_300">#9A7BD0</color>
<!-- Dracula green (#50fa7b), used for the protocol/type label. -->
<color name="color_dracula_green">#50FA7B</color>
<!-- Dracula red (#ff5555): failures / stopped state. -->
<color name="color_dracula_red">#FF5555</color>
<!-- Dracula yellow (#f1fa8c): profile name label. -->
<color name="color_dracula_yellow">#F1FA8C</color>

<color name="color_ng_black_accent">#9E9E9E</color>
<color name="color_ng_black_primary">#2B2B2B</color>
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
<!-- Base application theme. -->
<style name="Theme.SagerNet" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="actionBarStyle">@style/Widget.MaterialComponents.ActionBar.Solid</item>
<!-- Protocol/type label color; defaults to accent, Dracula overrides green. -->
<item name="protocolColor">?attr/accentOrTextSecondary</item>
<!-- Per-theme semantic colors; default to primary text (no visible change),
Dracula overrides them. -->
<item name="profileNameColor">?android:attr/textColorPrimary</item>
<item name="statusConnectedColor">?android:attr/textColorPrimary</item>
<item name="statusStoppedColor">?android:attr/textColorPrimary</item>
<item name="testFailColor">@color/material_red_500</item>
<!-- Default nav-drawer selected-item fill; also satisfies the
?itemShapeFillColor self-reference in @color/navigation_item so the
ColorStateList resolves for every theme. Only Black overrides it;
Expand Down Expand Up @@ -63,6 +71,11 @@

<style name="Theme.SagerNet.Dialog" parent="Theme.MaterialComponents.DayNight.Dialog.Alert">
<item name="actionBarStyle">@style/Widget.MaterialComponents.ActionBar.Solid</item>
<item name="protocolColor">?attr/accentOrTextSecondary</item>
<item name="profileNameColor">?android:attr/textColorPrimary</item>
<item name="statusConnectedColor">?android:attr/textColorPrimary</item>
<item name="statusStoppedColor">?android:attr/textColorPrimary</item>
<item name="testFailColor">@color/material_red_500</item>
<item name="itemShapeFillColor">?colorPrimary</item>
<item name="actionModeCloseDrawable">@drawable/ic_navigation_close</item>
<item name="colorButtonNormal">?colorAccent</item>
Expand Down Expand Up @@ -612,6 +625,11 @@
<item name="colorMaterial100">@color/color_dracula_100</item>
<item name="colorMaterial300">@color/color_dracula_300</item>
<item name="cardElevatedSurfaceColor">@color/card_elevated_dracula</item>
<item name="protocolColor">@color/color_dracula_green</item>
<item name="profileNameColor">@color/color_dracula_yellow</item>
<item name="statusConnectedColor">@color/color_dracula_green</item>
<item name="statusStoppedColor">@color/color_dracula_red</item>
<item name="testFailColor">@color/color_dracula_red</item>
</style>

<style name="Theme.SagerNet.Dialog.Black" parent="Theme.SagerNet.Dialog">
Expand Down Expand Up @@ -648,6 +666,7 @@
<item name="colorMaterial100">@color/color_dracula_100</item>
<item name="colorMaterial300">@color/color_dracula_300</item>
<item name="cardElevatedSurfaceColor">@color/card_elevated_dracula</item>
<item name="protocolColor">@color/color_dracula_green</item>
</style>
Comment thread
coderabbitai[bot] marked this conversation as resolved.

<style name="Theme.Start" parent="Theme.MaterialComponents.DayNight">
Expand Down
Loading