980 Commits

Author SHA1 Message Date
c7d3a64c34 Bump version to 1.40.2-typing.10
Some checks are pending
Trigger F-Droid repository rebuild / trigger (release) Waiting to run
2026-09-17 08:56:57 +02:00
c824b100b6 Make the clock agenda's calendars and row count configurable
The agenda part filtered calendars through the search settings and always
collapsed after four rows. It now has its own selection: an exclusion set
(`clockWidgetCalendarPartExcludedCalendars`, empty = all) that is added to the
search settings' exclusions, because the picker only offers calendars those
settings leave enabled.

The row cap is `clockWidgetCalendarPartMaxRows` (1..8, default 4); the agenda
shows that many events and then a "+N more events" row if more are left, so
with the default five events now render as four rows plus "+1 more event"
instead of three plus "+2".

Both are configured in `ConfigureCalendarPart`, shown by the clock sheet while
the Events part is enabled: a slider, a Calendars row that expands into a
colour-tinted checkbox per calendar, and a button to Settings -> Search ->
Calendar (new `ROUTE_SEARCH_CALENDARS`) for calendars the search settings
disable. A missing calendar permission shows a banner with a Grant button.

New setting fields default to the previous behaviour, so no datastore
migration is needed.
2026-09-17 08:56:55 +02:00
b0072f0fc2 Key the weather worker's location skip on the provider, not on a setting
typing.8 checked WeatherSettingsData.managedLocation, but that only exists once the
user has explicitly picked "managed location" for the provider, so
weatherProviderSettings stayed empty and the guard never fired: the worker kept
requesting a location and retrying every 30s.

The real signal is the provider's own capability. BreezyWeatherProvider.getWeatherData()
is a hardcoded noop that also manages its own location, so add WeatherProvider.isPushBased
and check that instead.

Also stop treating a managed location as "pull nothing": those providers still have data
to fetch, they just resolve the position themselves, so they now take the
WeatherLocation path instead of the auto-location one.

Bump to 1.40.2-typing.9 (versionCode 2026091202).
2026-09-12 15:43:10 +02:00
61f3424de5 Stop the weather worker pinning location for providers that manage it
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
Breezy Weather resolves its own location and pushes weather updates, but
WeatherUpdateWorker only looked at autoLocation before requesting a location.
BreezyWeatherProvider.getWeatherData() is a noop that always returns null, so
the worker then returned Result.retry() forever and never set lastUpdate -
which meant the update interval check could never short-circuit it either.

Every attempt called getLastKnownLocation(), which registers GPS *and* network
listeners at a one second interval and held them for up to ten minutes, so the
location stack never idled. On the phone this showed up as ~46k delivered fixes,
`*location*` wakelocks held ~99% of the time and Doze never engaging at all.

- expose the selected provider's managedLocation in WeatherSettingsData
- skip and cancel the weather work for providers that manage their own location
- give up on a fix after two minutes instead of ten
- LocationsRepository: don't collect the location flow when location search is
  off or not permitted. As a combineTransform argument it was always collected,
  so every search query registered GPS and network listeners for up to 30s.

Bump to 1.40.2-typing.8 (versionCode 2026091201).
2026-09-12 15:19:10 +02:00
291752abcd Bump version to 1.40.2-typing.7
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
The clock widget's agenda part is immediate and reliable now: its query is
no longer held back by the repository's 500 ms debounce, collectors no longer
receive an empty list before the provider has run, and the parts are not
rebuilt (losing their cached data) every time the launcher leaves the
background.
2026-09-11 21:21:28 +02:00
c8109d9197 Keep clock widget parts alive across lifecycle stops
`partProviders` was a `stateIn(WhileSubscribed)` flow, so its upstream
map ran again every time the clock widget was subscribed after the
launcher had been in the background - and built new part providers.
Providers cache what their ranking is based on, so the new agenda part
started at ranking 0, vanished while the launcher was gone and only came
back once its query had run again.

Build the provider list in `viewModelScope` into a `MutableStateFlow`
instead, so the instances (and their cached state) survive for as long
as the ViewModel does. Their ranking flows are still only collected
while the widget is subscribed, so observers are still registered and
unregistered with the widget's lifecycle.
2026-09-11 21:01:03 +02:00
3b3d6473ca Don't debounce the agenda query in the clock widget
`CalendarRepository.findMany` holds every result back by 500 ms, which
was added for the calendar widget to collapse bursts of plugin and
permission changes. `debounce` delays the first result too, so the
agenda part - whose ranking stays 0 until the first result arrives -
only appeared half a second after the clock and the other parts.

The agenda is re-queried on a calendar change or every 15 minutes, so
it does not need the debounce. Make the delay a parameter (still 500 ms
by default) and pass 0 from `CalendarPartProvider`.
2026-09-11 21:01:00 +02:00
6aba977823 Bump version to 1.40.2-typing.6
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
Alarm shows its trigger time and matches the date part's typography, static
dynamic-zone rankings (alarm 100, date 90, calendar 30), and a wider agenda
dot column.
2026-09-11 13:23:13 +02:00
229e5d1d32 Widen the agenda dot column to 24dp for more dot/time spacing 2026-09-11 13:22:59 +02:00
cb07acb577 Match the alarm part's typography to the date part
The alarm text used the button's default labelLarge in the vertical layout
and titleMedium in the compact one. Use titleMedium / titleLarge(Medium),
the same styles the date part uses, so both parts look alike.
2026-09-11 13:22:59 +02:00
a0f9413d40 Clock dynamic zone: alarm shows trigger time, static rankings, wider dot gap
- Alarm part now shows the time the alarm goes off instead of a relative
  "in X hours" span, formatted per the device's 12/24h setting.
- Reorder the dynamic zone: alarm (100) always sits below the clock, date
  (90) follows it.
- Calendar part ranking is now static (30) instead of boosting events that
  start within 30 minutes; drop the now-unused time plumbing.
- Widen the agenda dot column from 12dp to 20dp for more dot/time spacing.
2026-09-11 13:13:35 +02:00
05c7f0f60d Show the next alarm 24 hours ahead, not 8
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
An alarm set for the next morning left the dynamic zone before it was visible:
at 22:00 an alarm at 06:00 is eight hours away, so the part only appeared while
the phone was unlikely to be looked at. The window is now 24 hours, and the
setting summary says so (also in Danish; the other translations still say 8 and
will follow upstream).

Verified on the Titan 2 that Chrono (com.vicolo.chrono) registers its alarms the
way this part reads them: dumpsys alarm lists them as "Alarm clock" entries with
a showIntent pointing at Chrono, and AlarmManager.nextAlarmClock returns the
earliest one, so ACTION_NEXT_ALARM_CLOCK_CHANGED keeps the part up to date.
Tapping it opens Chrono as well - SHOW_ALARMS, SET_ALARM and SHOW_TIMERS all
resolve to com.vicolo.chrono/.MainActivity.
2026-09-11 12:30:05 +02:00
6c6cbe33fa Bump version to 1.40.2-typing.5
The agenda part and the configurable number of dynamic zone slots, with the
time column left-aligned.
2026-09-11 12:25:17 +02:00
6ae31fe92b Show an agenda in the clock widget's dynamic zone, with multiple slots
The dynamic zone showed exactly one part: the highest ranked of the enabled
ones. It now shows up to `clockWidgetDynamicZoneSlots` of them (default 1),
adjustable with a slider in the clock widget settings whose maximum is the
number of enabled parts. `ClockWidgetVM.getActiveParts()` returns that sorted
list, and both clock layouts render it (stacked under the clock, or next to it
in the horizontal layout).

On top of that the zone has an "Events" part (`clockWidgetCalendarPart`, off by
default) that shows today's agenda: events that are still running or upcoming
today, plus today's all-day events. At most four rows; when there are more, the
last row is a "+N more events" hint that opens the calendar app. Tapping a row
opens the event through `CalendarEvent.launch()`.

A row is three fixed-width columns that are pure layout, not drawn: a dot in the
calendar's colour, the time, and the title (ellipsized when it does not fit).
Times are always times, never dates - an all-day event renders "all-day" - and
the block is centred under the clock. The columns line up across rows, and the
time is left-aligned within its column.

The part ranks 70 when an event is running or starts within half an hour, 30
while events are left today and 0 on an empty day, so the date keeps the slot
when there is nothing to show. It reloads itself through a ContentObserver on
CalendarContract.Events and a 15 minute ticker, because unlike the calendar
widget it is on screen all the time. Tasks (`CalendarEvent.isTask`) are filtered
out for now; a tasks part can reuse this once tasks are configured.

Verified on the Titan 2: with the part enabled and three slots, the home screen
shows the agenda (two all-day events and a timed one) above the date part.
2026-09-11 12:25:09 +02:00
5c1e9c5bf2 Handle physical keyboard keys on the scaffold state that is rendered
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
Build Nightly APK / build (push) Has been cancelled
The key event handler was installed on the activity together with the state
instance it captured, so it could keep talking to a state that is no longer the
one that is drawn. That happens in practice: the scaffold state is recreated
when one of its rememberSaveable keys changes, which it does right after the
launcher starts (the window is measured, and the state is restored after the
configuration change), and the activity then still holds the handler of the
previous state. Typing on the home screen set the search query and started the
page transition on the detached state, so the search page never appeared, while
the query showed up in the search bar.

The handler is now created without the state as a key and asks for the current
state through a provider, and the activity's handler is refreshed on every
composition instead of only when the handler instance changes.

Verified on the Titan 2: typing on the home screen opens the search page with
the character inserted, further characters append, the text field stays
unfocused and the on-screen keyboard stays hidden.
2026-09-10 21:44:46 +02:00
e004862b00 Bump version to 1.40.2-typing.3
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
Fixes on top of -typing.2: the widget configure activity crash, the search bar
keeping the keyboard focus (and the IME) on the home screen after an app switch,
and the TextFieldValue update in SearchBar moving out of the composition.
2026-09-10 20:46:11 +02:00
ff3cab9531 Keep the search bar from holding the keyboard focus on the home screen
Returning to the launcher from another app left the search bar's text field
focused, with the on-screen keyboard up. Typing on the physical keyboard then
went into the search bar - the IME commits characters to the focused text field
- while the search results never appeared, because the key event handler
deliberately leaves keys alone while the text field has the focus. Coming home
looked like the launcher had stopped responding to the keyboard; it happens with
a freshly started launcher and a plain app switch, with no search ever opened.

Give that focus up again one frame after the launcher is resumed, and hide the
on-screen keyboard, as long as the home screen is what is shown. Only on a
resume (pauseTime > 0), so that a search component used as the home screen can
still focus itself when the launcher starts.

Also set isSearchBarFocused back to false when the text field loses the focus,
like upstream does. LauncherSearchBar requests focus whenever that flag is true,
so only ever setting it to true made the text field focus itself again after it
had been unfocused - which is what kept the on-screen keyboard around.

Clear the search query before resetting the page in the resume path, so that a
stale query is gone even if the page transition is interrupted.

Note: the focus fix is a workaround for the framework/IME handing the focus back
to the text field; it is not verified on the device yet.
2026-09-10 20:41:05 +02:00
79f229de6d SearchBar: adopt outside text changes in a side effect
SearchBar keeps a TextFieldValue so that the cursor ends up behind text that was
changed from the outside (a search started by typing on a physical keyboard, or
a reset search). That value was updated during composition, which is an
anti-pattern: the value read earlier in the same composition is what gets drawn,
so the write only schedules another recomposition pass of the search bar - and
it can be dropped entirely if the composition does not complete.

Do the same update in a SideEffect, after the composition. No behaviour change
for the user.
2026-09-10 20:41:01 +02:00
73c3683740 Don't crash when a widget has no configure activity
A widget can advertise WIDGET_FEATURE_RECONFIGURABLE without providing a
configure activity (or the app providing it was uninstalled since). Tapping
"Configure widget" then made
AppWidgetHost.startAppWidgetConfigureActivityForResult throw, which killed the
launcher:

  android.content.ActivityNotFoundException: No Activity found to handle null
    at android.app.Activity.startIntentSenderForResult
    at android.appwidget.AppWidgetHost.startAppWidgetConfigureActivityForResult
    at ConfigureWidgetSheetKt.ConfigureAppWidget$lambda$10$1$0(ConfigureWidgetSheet.kt:812)

This happened three times on the Titan 2 on 2026-09-10 (19:55, 19:57 and 20:03,
the last one 28 seconds after boot). Catch ActivityNotFoundException, and
SecurityException for a configure activity that refuses to be started, and log
instead. Upstream main still has the same unguarded call.
2026-09-10 20:40:57 +02:00
e17903f40f Clear the search when the launcher is left, end keyboard search on dismiss
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
Two fixes on top of the type-to-search feature:

- The search no longer survives leaving the launcher. Upstream only resets the
  search when the launcher was in the background for more than five seconds, so
  a quick app switch or a fast return from a launched app left the old query on
  screen. The search page is now always reset, whatever the pause was.
- Dismissing the search page ends the keyboard driven search. Without that the
  flag stayed set, so opening the search later (gesture or search bar) did not
  focus the text field and the soft keyboard stayed away.

Version 1.40.2-typing.2.
2026-09-10 12:03:00 +02:00
0f53f7ecdf Open the search when typing on a physical keyboard
Some checks failed
Trigger F-Droid repository rebuild / trigger (release) Has been cancelled
Typing on a physical keyboard while the home screen is shown now opens the
search page and inserts the typed character, so the launcher can be used
without touching the screen. It works for any printable key, appends the
characters in order even when the user types faster than the launcher can
recompose, and the search bar can still be tapped to edit the query with
the on-screen keyboard as usual.

While the search is driven by the keyboard, the search bar's text field is
left unfocused on purpose: a focused text field would make the system show
the on-screen keyboard, which is not wanted when there is a physical
keyboard. The handler therefore inserts and deletes characters itself,
including backspace and enter (to launch the best match).

The behaviour is controlled by a new "Search on typing" preference in
Settings -> Search, which is enabled by default.

Implementation notes:
- The key events are intercepted in SharedLauncherActivity.dispatchKeyEvent,
  which is the only place that sees hardware key events while no view has
  focus. The scaffold installs the handler while it is shown.
- Key events are not forwarded to the search bar's text field, so the
  characters are inserted into the search view model directly.
- SearchBar keeps a TextFieldValue instead of a String so that the cursor
  moves to the end of the text when the value is changed from the outside.
  Without this the next typed character ended up in front of the text.
2026-09-10 11:45:14 +02:00
MM20
32170cfc48 show badges in iconless app list
close #1982
2026-08-30 17:04:59 +02:00
MM20
4dff3b5ee6 make dropdown menus scrollable
fix #1906
2026-08-30 16:53:22 +02:00
MM20
9b8b083515 open calendar app and weather app in new task
fix #1963
2026-08-30 16:14:23 +02:00
MM20
5bd8b832f4 calendar widget: open calendar at selected date 2026-08-30 16:12:03 +02:00
MM20
728795d7bc refactor media integration settings to use lazy items 2026-08-23 18:28:32 +02:00
MM20
17f8906f26 add setting to exclude apps from shortcuts search 2026-08-23 17:55:08 +02:00
MM20
37f738b129 Remove unused code 2026-08-16 15:52:54 +02:00
MM20
37ccc83e7c remove accompanist libraries and compose icons 2026-08-16 15:36:16 +02:00
MM20
64dc66aadb Update dependencies, change java version to 11, update Gradle 2026-08-12 23:42:41 +02:00
MM20
057de087e9 Use all system locales to determine default currencies 2026-08-11 21:48:20 +02:00
MM20
c255894f3b allowlist belarusian in form of address preference 2026-08-11 00:00:52 +02:00
MM20
a92e10e827 use progressive blur effect
close #1969
2026-08-01 16:10:30 +02:00
MM20
a94c1cf285 clear search when locking/unlocking private space 2026-07-20 22:17:18 +02:00
MM20
f5269845c8 fix crash when locking hidden private space 2026-07-19 21:39:34 +02:00
MM20
3f4deead1b (refactor) refactor ProfileManager class 2026-07-19 18:27:59 +02:00
Alex Alwardt
db051292be Feature: Respect hidden private space option (#1923)
* Hide private profile space apps from search when hide private space option is enabled, and add a search action for locking and unlocking the private space.

* Filter private space apps from favorites while hidden
2026-07-04 15:57:54 +02:00
MM20
8b50c8349d add currency settings 2026-06-29 19:13:30 +02:00
MM20
b1b2daa67d Add MainSwitchPreference component 2026-06-26 13:01:47 +02:00
MM20
5b4aade52f launch settings in new task 2026-06-21 21:51:26 +02:00
MM20
1e3e0911d9 change quick settings action icon 2026-06-21 21:36:58 +02:00
MM20
ded17a1af3 Add launcher settings gesture action 2026-06-20 17:09:32 +02:00
MM20
8e236e1d1d bump version code and add changelog 2026-05-31 18:57:34 +02:00
MM20
7e7f3f72aa Version 1.40.2 2026-05-31 18:15:11 +02:00
MM20
f186fca7c6 Allow custom widget watch faces to be full width 2026-05-31 17:20:39 +02:00
MM20
be745484c5 fix empty bottom sheets closing themselves 2026-05-31 17:06:44 +02:00
MM20
9953139619 invert hide app details preference 2026-05-31 13:32:46 +02:00
Snow
9017a06239 feat : Add setting to toggle app version visibility in grid popup (#1843)
* Add gridAppVersion setting and wiring

* Implement app version visibility toggle in grid popup

* Add hideAppDetails setting and wiring

* feat : add hideAppDetails toggle and remove gridShowAppVersion
2026-05-31 13:22:32 +02:00
MM20
b071668743 fix swipe gestures not working after double tap to sleep
fix #1879
2026-05-31 13:11:59 +02:00
MM20
e25f2db3b5 refactor launcher scaffold 2026-05-27 21:12:40 +02:00