Compare commits

...

3 Commits

Author SHA1 Message Date
e08dc5744d fix(theme): keep the bar-icon override across config-change replays (#70) 2026-07-31 20:26:14 +02:00
ce740c39b1 fix(theme): drive system bar icons from the in-app light/dark choice (#70) 2026-07-31 19:59:48 +02:00
Jean-Luc Makiola
0097a9c534 Fix the Play upload's AAB path (#110)
Some checks failed
Release — F-Droid repo + Gitea/Codeberg release + Play / detect (push) Failing after 5s
Release — F-Droid repo + Gitea/Codeberg release + Play / release (push) Has been skipped
Release — F-Droid repo + Gitea/Codeberg release + Play / play (push) Has been skipped
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/110
2026-07-31 12:37:13 +02:00
3 changed files with 46 additions and 4 deletions

View File

@@ -635,9 +635,13 @@ jobs:
FASTLANE_HIDE_CHANGELOG: '1' FASTLANE_HIDE_CHANGELOG: '1'
run: | run: |
set -euo pipefail set -euo pipefail
test -f "dist/app-release.aab" AAB="$GITHUB_WORKSPACE/dist/app-release.aab"
# Absolute, because a lane body runs from fastlane/, not the
# workspace root — a relative path resolves against the wrong
# directory there and 2.17.1 died on exactly that.
test -f "$AAB" || { echo "No AAB at $AAB — the artifact handoff failed." >&2; ls -la dist || true; exit 1; }
bundle exec fastlane deploy \ bundle exec fastlane deploy \
aab:"dist/app-release.aab" \ aab:"$AAB" \
track:"$PLAY_TRACK" \ track:"$PLAY_TRACK" \
release_status:"$PLAY_RELEASE_STATUS" \ release_status:"$PLAY_RELEASE_STATUS" \
dry_run:"$PLAY_DRY_RUN" dry_run:"$PLAY_DRY_RUN"

View File

@@ -2,9 +2,11 @@ package de.jeanlucmakiola.calendula
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.CalendarContract import android.provider.CalendarContract
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -12,6 +14,7 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -53,6 +56,11 @@ import kotlin.time.Instant
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
// Which of light/dark the system bars are drawn for. The styles installed
// in onCreate read this field live, so androidx's config-change replay
// picks up the in-app override instead of the night resource qualifier.
private var systemBarsDark = false
// The occurrence a reminder notification was tapped for (eventId, begin, // The occurrence a reminder notification was tapped for (eventId, begin,
// end — the detail screen's key shape). singleTop + onNewIntent route a // end — the detail screen's key shape). singleTop + onNewIntent route a
// tap into the running activity; CalendarHost consumes and clears it. // tap into the running activity; CalendarHost consumes and clears it.
@@ -96,7 +104,9 @@ class MainActivity : AppCompatActivity() {
return return
} }
enableEdgeToEdge() systemBarsDark = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
Configuration.UI_MODE_NIGHT_YES
applyEdgeToEdge()
requestedDetailKey = intent.detailKeyOrNull() ?: intent.viewEventKeyOrNull() requestedDetailKey = intent.detailKeyOrNull() ?: intent.viewEventKeyOrNull()
requestedNav = intent.navRequestOrNull() requestedNav = intent.navRequestOrNull()
requestedImportUri = intent.importUriOrNull() requestedImportUri = intent.importUriOrNull()
@@ -113,6 +123,14 @@ class MainActivity : AppCompatActivity() {
ThemeMode.LIGHT -> false ThemeMode.LIGHT -> false
ThemeMode.DARK -> true ThemeMode.DARK -> true
} }
// onCreate can only see the night resource qualifier, not the in-app
// override — re-apply from the resolved theme so the bar icons follow
// the app's light/dark choice.
DisposableEffect(darkTheme) {
systemBarsDark = darkTheme
applyEdgeToEdge()
onDispose {}
}
// The app-wide clock convention: the time-format preference resolved // The app-wide clock convention: the time-format preference resolved
// against the device's 24-hour system setting, provided once here so // against the device's 24-hour system setting, provided once here so
// every time label reads it via LocalUse24HourFormat. // every time label reads it via LocalUse24HourFormat.
@@ -181,6 +199,19 @@ class MainActivity : AppCompatActivity() {
} }
} }
/**
* Applies the transparent edge-to-edge bars for the current [systemBarsDark].
* Both scrims are transparent because API 29+ enforces its own contrast and
* ignores them anyway.
*/
private fun applyEdgeToEdge() {
val transparent = android.graphics.Color.TRANSPARENT
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto(transparent, transparent) { systemBarsDark },
navigationBarStyle = SystemBarStyle.auto(transparent, transparent) { systemBarsDark },
)
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
// Reaching a running UI means startup succeeded; reset the loop trail. // Reaching a running UI means startup succeeded; reset the loop trail.

View File

@@ -14,10 +14,17 @@
default_platform(:android) default_platform(:android)
# fastlane runs a lane body with the working directory set to `fastlane/`, not
# the project root, so a relative artifact path from the caller has to be
# resolved explicitly. Absolute paths pass through untouched.
def project_path(path)
File.expand_path(path, File.expand_path("..", FastlaneCore::FastlaneFolder.path || "."))
end
platform :android do platform :android do
desc "Upload an already-built, already-signed AAB to Play" desc "Upload an already-built, already-signed AAB to Play"
lane :deploy do |options| lane :deploy do |options|
aab = options[:aab] || "app/build/outputs/bundle/release/app-release.aab" aab = project_path(options[:aab] || "app/build/outputs/bundle/release/app-release.aab")
UI.user_error!("AAB not found at #{aab}") unless File.exist?(aab) UI.user_error!("AAB not found at #{aab}") unless File.exist?(aab)
supply( supply(