Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/src/main/java/net/ardevd/tagius/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.first
import com.google.android.material.color.DynamicColors
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import net.ardevd.tagius.core.data.TokenManager
import net.ardevd.tagius.databinding.ActivityMainBinding
import net.ardevd.tagius.features.auth.ui.LoginFragment
Expand Down Expand Up @@ -72,6 +74,14 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Dynamic color (if set)
runBlocking {
val tokenManager = TokenManager(applicationContext)
val useDynamicColors = tokenManager.dynamicColorsFlow.first()
if (useDynamicColors) {
DynamicColors.applyToActivitiesIfAvailable(application)
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DynamicColors.applyToActivitiesIfAvailable(application) registers lifecycle callbacks and is typically intended to be called from Application.onCreate() (before any Activity is created). Calling it inside MainActivity.onCreate() is likely too late for the current activity, so dynamic colors may not be applied at all in a single-activity app. Prefer applying dynamic colors in an Application subclass, or use the activity-scoped API (e.g., apply to the current activity) so this activity actually gets the dynamic theme.

Suggested change
DynamicColors.applyToActivitiesIfAvailable(application)
DynamicColors.applyToActivityIfAvailable(this)

Copilot uses AI. Check for mistakes.
}
}
Comment on lines +77 to +84
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using runBlocking on the main thread in onCreate can block UI startup and risks ANRs if DataStore I/O is slow (or if the first read is delayed). Consider a non-blocking approach (e.g., read from a synchronous source for this startup-only decision, or move the preference read + dynamic-color setup into app startup code where it won’t block the UI thread).

Copilot uses AI. Check for mistakes.
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/net/ardevd/tagius/core/data/TokenManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package net.ardevd.tagius.core.data
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
Expand All @@ -21,6 +22,7 @@ class TokenManager(private val context: Context) {
private val KEY_SERVER_URL = stringPreferencesKey("server_url")
private val KEY_LAST_DESCRIPTION = stringPreferencesKey("last_description")
private val KEY_LAST_ZOMBIE_ID = stringPreferencesKey("last_zombie_id")
private val KEY_DYNAMIC_COLORS = booleanPreferencesKey("dynamic_colors")
private const val DEFAULT_URL = "https://timetagger.app/"

}
Expand Down Expand Up @@ -84,4 +86,15 @@ class TokenManager(private val context: Context) {
val baseAPIURI = LoginRetrofitClient.determineApiPath(baseUrl)
baseUrl + baseAPIURI
}

val dynamicColorsFlow: Flow<Boolean> = context.dataStore.data.map { preferences ->
preferences[KEY_DYNAMIC_COLORS] ?: true
}

// Write Function
suspend fun saveDynamicColors(enabled: Boolean) {
context.dataStore.edit { preferences ->
preferences[KEY_DYNAMIC_COLORS] = enabled
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,25 @@ class SettingsBottomSheet(
private var _binding: FragmentSettingsBinding? = null
private val binding get() = _binding!!

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSettingsBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val tokenManager = TokenManager(requireContext())
// Show version info
val version = BuildConfig.VERSION_NAME
binding.versionText.text = "v$version"

// Display the stored URL so the user knows which server they are on
viewLifecycleOwner.lifecycleScope.launch {
val url = TokenManager(requireContext()).serverUrlFlow.first()
val url = tokenManager.serverUrlFlow.first()
binding.serverUrlText.text = url
}

Expand All @@ -42,6 +46,18 @@ class SettingsBottomSheet(
dismiss()
onLogout()
}

// System Color setting handling
viewLifecycleOwner.lifecycleScope.launch {
val useDynamicColors = tokenManager.dynamicColorsFlow.first()
binding.dynamicColorsSwitch.isChecked = useDynamicColors
}

binding.dynamicColorsSwitch.setOnCheckedChangeListener { _, isChecked ->
viewLifecycleOwner.lifecycleScope.launch {
tokenManager.saveDynamicColors(isChecked)
Comment on lines +53 to +58
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coroutine that sets dynamicColorsSwitch.isChecked can resume after the setOnCheckedChangeListener is attached, which will trigger the listener and persist a value even though it’s just initializing UI state. Consider setting the initial checked state before attaching the listener, or temporarily removing/suppressing the listener while updating isChecked.

Suggested change
binding.dynamicColorsSwitch.isChecked = useDynamicColors
}
binding.dynamicColorsSwitch.setOnCheckedChangeListener { _, isChecked ->
viewLifecycleOwner.lifecycleScope.launch {
tokenManager.saveDynamicColors(isChecked)
binding.dynamicColorsSwitch.setOnCheckedChangeListener(null)
binding.dynamicColorsSwitch.isChecked = useDynamicColors
binding.dynamicColorsSwitch.setOnCheckedChangeListener { _, isChecked ->
viewLifecycleOwner.lifecycleScope.launch {
tokenManager.saveDynamicColors(isChecked)
}

Copilot uses AI. Check for mistakes.
}
}
}

companion object {
Expand Down
14 changes: 13 additions & 1 deletion app/src/main/res/layout/fragment_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/dynamicColorsSwitch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/settings_dynamic_colors"
android:textAppearance="?attr/textAppearanceLabelLarge"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="24dp"
app:layout_constraintTop_toBottomOf="@id/serverCard"
app:layout_constraintStart_toStartOf="@id/serverCard" />
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dynamicColorsSwitch is set to layout_width="0dp" but only has a start constraint. In ConstraintLayout, 0dp (“match constraints”) requires both start and end constraints; otherwise the view may measure to 0 width or cause layout warnings. Add an end constraint (e.g., constrain end to serverCard/parent) or change the width to wrap_content/match_parent consistently with your desired layout.

Suggested change
app:layout_constraintStart_toStartOf="@id/serverCard" />
app:layout_constraintStart_toStartOf="@id/serverCard"
app:layout_constraintEnd_toEndOf="@id/serverCard" />

Copilot uses AI. Check for mistakes.

<com.google.android.material.button.MaterialButton
android:id="@+id/logoutButton"
style="@style/Widget.Material3.Button.TonalButton"
Expand All @@ -67,7 +79,7 @@
android:layout_marginTop="32dp"
android:text="@string/settings_log_out"
app:icon="@drawable/ic_logout"
app:layout_constraintTop_toBottomOf="@id/serverCard" />
app:layout_constraintTop_toBottomOf="@id/dynamicColorsSwitch" />

<TextView
android:id="@+id/versionText"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<string name="settings_connected_to">Connected to</string>
<string name="settings_log_out">Log Out</string>
<string name="settings_settings">Settings</string>
<string name="settings_dynamic_colors">Use System Colors</string>

<string name="error_invalid_token">Invalid API Token provided</string>
<string name="error_api_not_found">API not found at this URL</string>
Expand Down
Loading