Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 273cfce969 | |||
| b6a45b7264 |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2.11.1] — 2026-06-28
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Calendula can now be set as your default calendar app. It registers the
|
||||||
|
calendar-app intent filters the system uses, so it appears in the chooser when
|
||||||
|
you tap a date in a launcher or clock — and opening one takes you straight to
|
||||||
|
that day. Android has no way for an app to make itself the default, so you pick
|
||||||
|
it once from the system picker. Thanks to @abrossimow for the report ([#9]).
|
||||||
|
|
||||||
## [2.11.0] — 2026-06-27
|
## [2.11.0] — 2026-06-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -685,3 +694,4 @@ automatically, with zero telemetry and no internet permission.
|
|||||||
[#6]: https://codeberg.org/jlmakiola/calendula/issues/6
|
[#6]: https://codeberg.org/jlmakiola/calendula/issues/6
|
||||||
[#7]: https://codeberg.org/jlmakiola/calendula/issues/7
|
[#7]: https://codeberg.org/jlmakiola/calendula/issues/7
|
||||||
[#8]: https://codeberg.org/jlmakiola/calendula/issues/8
|
[#8]: https://codeberg.org/jlmakiola/calendula/issues/8
|
||||||
|
[#9]: https://codeberg.org/jlmakiola/calendula/issues/9
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ android {
|
|||||||
// which builds this version and then creates the matching vX.Y.Z tag +
|
// which builds this version and then creates the matching vX.Y.Z tag +
|
||||||
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
|
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
|
||||||
// PATCH from versionName, e.g. 2.7.2 -> 20702). See docs/RELEASING.md.
|
// PATCH from versionName, e.g. 2.7.2 -> 20702). See docs/RELEASING.md.
|
||||||
versionCode = 21100
|
versionCode = 21101
|
||||||
versionName = "2.11.0"
|
versionName = "2.11.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,35 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Be selectable as the system calendar app. Android has no API for
|
||||||
|
an app to make itself the default, so registering the filters
|
||||||
|
launchers and the OS use is what lets the user pick Calendula
|
||||||
|
from the system chooser when a date action fires (issue #9).
|
||||||
|
APP_CALENDAR is the "open the calendar app" action; the VIEW
|
||||||
|
filters below catch a tapped date. -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.APP_CALENDAR" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- A launcher/clock date tap fires ACTION_VIEW on the provider's
|
||||||
|
time Uri (content://com.android.calendar/time/<epochMillis>);
|
||||||
|
some surfaces use the time/epoch mime type. We open the day view
|
||||||
|
on that date (MainActivity.calendarTimeDateOrNull). -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data
|
||||||
|
android:scheme="content"
|
||||||
|
android:host="com.android.calendar"
|
||||||
|
android:pathPrefix="/time" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="time/epoch" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Open a .ics file (file manager / email attachment / browser). -->
|
<!-- Open a .ics file (file manager / email attachment / browser). -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ import de.jeanlucmakiola.calendula.ui.crash.submitCrashReport
|
|||||||
import de.jeanlucmakiola.calendula.ui.settings.SettingsViewModel
|
import de.jeanlucmakiola.calendula.ui.settings.SettingsViewModel
|
||||||
import de.jeanlucmakiola.calendula.ui.theme.CalendulaTheme
|
import de.jeanlucmakiola.calendula.ui.theme.CalendulaTheme
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.toLocalDateTime
|
||||||
|
import kotlin.time.Instant
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
@@ -156,10 +159,33 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Intent.ACTION_SEND -> IntentCompat.getParcelableExtra(this, Intent.EXTRA_STREAM, Uri::class.java)
|
Intent.ACTION_SEND -> IntentCompat.getParcelableExtra(this, Intent.EXTRA_STREAM, Uri::class.java)
|
||||||
else -> null
|
else -> null
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
// The calendar "view time" Uri (a date tap) is also ACTION_VIEW/content;
|
||||||
|
// it's a navigation, not a file to import, so [navRequestOrNull] owns it.
|
||||||
|
if (uri.host == CALENDAR_PROVIDER_HOST) return null
|
||||||
return uri.takeIf { it.scheme == "content" || it.scheme == "file" }
|
return uri.takeIf { it.scheme == "content" || it.scheme == "file" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date a launcher/clock date tap points at, parsed from the AOSP calendar
|
||||||
|
* "view time" intent: ACTION_VIEW on `content://com.android.calendar/time/
|
||||||
|
* <epochMillis>`. Null for any other intent. The matching manifest filter is
|
||||||
|
* what lets users pick Calendula from the system calendar chooser (issue #9).
|
||||||
|
*/
|
||||||
|
private fun Intent.calendarTimeDateOrNull(): LocalDate? {
|
||||||
|
if (action != Intent.ACTION_VIEW) return null
|
||||||
|
val uri = data ?: return null
|
||||||
|
if (uri.host != CALENDAR_PROVIDER_HOST) return null
|
||||||
|
val segments = uri.pathSegments
|
||||||
|
if (segments.firstOrNull() != "time") return null
|
||||||
|
val millis = segments.getOrNull(1)?.toLongOrNull() ?: return null
|
||||||
|
return Instant.fromEpochMilliseconds(millis)
|
||||||
|
.toLocalDateTime(TimeZone.currentSystemDefault()).date
|
||||||
|
}
|
||||||
|
|
||||||
private fun Intent.navRequestOrNull(): WidgetNavRequest? {
|
private fun Intent.navRequestOrNull(): WidgetNavRequest? {
|
||||||
|
// An external date tap (launcher/clock) has no widget source, so it opens
|
||||||
|
// the day view rooted over the default home view (OpenDate source = null).
|
||||||
|
calendarTimeDateOrNull()?.let { return WidgetNavRequest.OpenDate(it.toString(), source = null) }
|
||||||
val source = sourceViewOrNull()
|
val source = sourceViewOrNull()
|
||||||
val eventId = getLongExtra(EXTRA_EVENT_ID, -1L)
|
val eventId = getLongExtra(EXTRA_EVENT_ID, -1L)
|
||||||
return when {
|
return when {
|
||||||
@@ -201,6 +227,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
// The calendar provider's authority/host. A date tap arrives as
|
||||||
|
// ACTION_VIEW on content://com.android.calendar/time/<epochMillis>.
|
||||||
|
private const val CALENDAR_PROVIDER_HOST = "com.android.calendar"
|
||||||
|
|
||||||
private const val EXTRA_EVENT_ID = "de.jeanlucmakiola.calendula.extra.EVENT_ID"
|
private const val EXTRA_EVENT_ID = "de.jeanlucmakiola.calendula.extra.EVENT_ID"
|
||||||
private const val EXTRA_BEGIN_MILLIS = "de.jeanlucmakiola.calendula.extra.BEGIN"
|
private const val EXTRA_BEGIN_MILLIS = "de.jeanlucmakiola.calendula.extra.BEGIN"
|
||||||
private const val EXTRA_END_MILLIS = "de.jeanlucmakiola.calendula.extra.END"
|
private const val EXTRA_END_MILLIS = "de.jeanlucmakiola.calendula.extra.END"
|
||||||
|
|||||||
@@ -196,7 +196,9 @@ fun CalendarHost(
|
|||||||
dismissCoveringOverlays()
|
dismissCoveringOverlays()
|
||||||
createDateIso = null
|
createDateIso = null
|
||||||
pendingDayIso = req.dateIso
|
pendingDayIso = req.dateIso
|
||||||
viewStack = viewBaseStack(defaultView, req.source).drillToDay()
|
// No widget source (an external date tap) roots over the default
|
||||||
|
// home view, so backing out of the day returns home then exits.
|
||||||
|
viewStack = viewBaseStack(defaultView, req.source ?: defaultView).drillToDay()
|
||||||
onWidgetNavConsumed()
|
onWidgetNavConsumed()
|
||||||
}
|
}
|
||||||
is WidgetNavRequest.OpenEvent -> {
|
is WidgetNavRequest.OpenEvent -> {
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
|||||||
* detail-key channel and leave the base view untouched.)
|
* detail-key channel and leave the base view untouched.)
|
||||||
*/
|
*/
|
||||||
sealed interface WidgetNavRequest {
|
sealed interface WidgetNavRequest {
|
||||||
/** Open the day view anchored on [dateIso] (an ISO `yyyy-MM-dd` date), over [source]. */
|
/**
|
||||||
data class OpenDate(val dateIso: String, val source: CalendarView) : WidgetNavRequest
|
* Open the day view anchored on [dateIso] (an ISO `yyyy-MM-dd` date), over
|
||||||
|
* [source]. A null [source] means the request came from outside the app (a
|
||||||
|
* launcher/clock date tap, issue #9) rather than a widget, so it roots over
|
||||||
|
* the default home view instead of a widget's view.
|
||||||
|
*/
|
||||||
|
data class OpenDate(val dateIso: String, val source: CalendarView?) : WidgetNavRequest
|
||||||
|
|
||||||
/** Open one occurrence's detail (an agenda-widget event tap), over [source]. */
|
/** Open one occurrence's detail (an agenda-widget event tap), over [source]. */
|
||||||
data class OpenEvent(
|
data class OpenEvent(
|
||||||
|
|||||||
6
fastlane/metadata/android/en-US/changelogs/21101.txt
Normal file
6
fastlane/metadata/android/en-US/changelogs/21101.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
### Fixed
|
||||||
|
- Calendula can now be set as your default calendar app. It registers the
|
||||||
|
calendar-app intent filters the system uses, so it appears in the chooser when
|
||||||
|
you tap a date in a launcher or clock — and opening one takes you straight to
|
||||||
|
that day. Android has no way for an app to make itself the default, so you pick
|
||||||
|
it once from the system picker. Thanks to @abrossimow for the report ([#9]).
|
||||||
Reference in New Issue
Block a user