2406 Commits
Author SHA1 Message Date
TMCandClaude Opus 5.5 f5d7cf07ed Line up every widget with the icons, as the Pictures widget does
Widgets of other apps used to fill their cells minus Launcher3's widget
padding, which is a few pixels wider than the icons' edges and reaches down
into the room kept for icon labels; on the owner's phone they hung about
13 px past the icons sideways and 40 px below. Now the room a widget keeps in
its cells is the distance from the cell edges to the icon edges, worked out
as IconFrame does for the launcher's own widgets, so every widget's edges
meet the edges of the icons around it.

The one function (FlexeraWidgets.appWidgetPadding) is used for the layout,
for the size the app is told, for the drop animation and through
WidgetSizes for previews. The cells a widget needs are still worked out
with Launcher3's padding, so no widget changes its cell count. A new switch
under Settings, Home screen, Widgets ("Line up with icons", on by default)
turns it off; Lawnchair's Widget padding slider shows only then.

Checked on the emulator against lines drawn at the icon edges: Google
Calendar's widgets meet them within 2 px; Google Clock's sits about 20 px
inside, because it draws an empty margin of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 12:19:33 +01:00
TMCandClaude Fable 5.2 77278a11e0 Hide app from the app list's long-press menu; stop the restart on the first preference write
Hide app: a new entry in the menu a long press on an app opens in the app
list. It adds the app to Lawnchair's hidden list (hidden_apps), which the
list and its search already leave out and which Settings > App drawer >
Hidden apps already shows, so that page is where an app is brought back.
A message with Undo follows. The entry is offered in the app list only
(not for home screen icons, which hiding does not touch) and not while
the home screen is locked.

Preference collectors: five collectors in PreferenceManager2 (icon shape,
dock mode, dock on/off, dock search provider, labels in the dock) had
drop(1) before distinctUntilChanged. The first emission after the dropped
one then always passed, so the first write of any preference after the
launcher started reloaded the model, rebuilt the grid three times and
recreated the launcher. Hiding an app is such a write, made while the
launcher is on screen: the list went blank for a moment and the Undo
message was lost with the old activity. The operators are swapped, so
only a changed value passes.

Checked on the emulator: both menu layouts (labelled rows and the icon
row), hiding right after a fresh start (no relaunch in the log, message
stays), Undo, and unticking the apps under Hidden apps.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 20:21:59 +01:00
TMCandClaude Fable 5.2 3b7388c473 Blur behind open folders; hide the icons of a switched-off dock
Folder blur: while a folder is open on the home screen, what is behind it
is blurred, icons and widgets through a RenderEffect and the wallpaper
through the launcher surface's background blur. It is a new input of the
depth controller (folderBlur, 0 to 1), because that controller already
owns both kinds of blur and cleared anything set past it on every frame
of the folder's wallpaper zoom. The folder's spring animation drives it in
step with the dimming, and the places that reset the dimming reset it too.
It works with the wallpaper depth effect switched off, without bringing
the folder's wallpaper zoom along. Setting: Folders > Blur behind open
folders, on by default (flexera_folder_blur). Folders in the app list are
left as they were.

Dock: with the dock switched off the hotseat has no height, but its icons
were still drawn just below the screen edge, and the 97.5% zoom-out behind
an open folder brought their tops into view. One more alpha channel on
the icons hides them.

Checked on the emulator (Android 17 image, window blur available): open,
close by tap outside, Back and Home, an app started from the folder, sleep
and wake, the setting off, and the depth effect off.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 18:01:33 +01:00
TMCandClaude Fable 5.2 fba65e091f Add quick launch: a swipe past the first or last home page opens a chosen app
Swiping on from the first home screen page, towards the page that is not
there, now does what the owner chose under Settings, Gestures, Quick launch:
open an app, or any other of the launcher's gesture actions. The last page has
a choice of its own.

The stretch the pages already show at either end is an edge effect; quick
launch replaces it with one that also counts how far the finger has pulled.
Lifting the finger after a pull of about a seventh of the screen, or after a
flick, does the action; a short pull or one that is taken back does nothing.
An app opens out of the screen edge the finger pulled from.

From Android 12, where the stretch is Android's own. Infinite scrolling takes
those swipes for itself, so the two do not combine.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 14:36:03 +01:00
TMCandClaude Fable 5.2 d5a62cc6f5 Redraw every open copy of the launcher when a setting changes
Opening the launcher like an app (the installer's "Open") and then making it
the home app leaves two launcher activities in one process. A changed setting
recreated whichever one LawnchairApp.launcher returned, which could be the one
in the background, so the home screen kept its old look until a restart.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 13:04:33 +01:00
TMCandClaude Fable 5.2 d0d1c031d0 Add a row size setting and give the screen edge back to Android's Back swipe
Row size (Settings, App drawer, Style): Compact is the list as it was, Comfy
has taller rows, larger icons and slightly larger names and letter headers.

In the open drawer the alphabet no longer reserves the screen edge. Its letters
rest 30dp from the edge, clear of the strip where Android starts Back. A touch
that starts on that strip becomes a scrub once it moves up or down or rests for
a moment; a sideways swipe is left to Android and goes back. The band that
starts a scrub from the home screen is narrower than the one in the drawer.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 12:09:11 +01:00
TMCandClaude Fable 5.2 d82d488189 Import home screens from a Smart Launcher backup; survive a grid reload with unset widgets
Asked for by the owner on 12 November 2026, with one of their own backups to work from.

Import (Settings > Backup and restore > Import from Smart Launcher)
- A .slbk file is a zip of Smart Launcher's files. The importer reads its database
  (HomeItem, Launchable, Action, Widget) and the grid from its preferences, and writes the
  home screens as a Launcher3 database that Launcher3's restore code takes in, the way
  Lawnchair's Nova import does. The format is not documented; this follows one real
  backup of data version 64 and refuses a file without those tables.
- One to one, by the owner's decision: every icon, folder and widget in the cell it had, on
  a grid of the same size, and the dock switched off because Smart Launcher has none.
  Home screen labels follow the backup.
- Widgets arrive as "tap to set up". Android has each launcher ask for its own widgets and
  keeps a widget's settings with the launcher that made it.
- Left out, and said so on the screen before the owner agrees: Smart Launcher's own widgets,
  items of other profiles, anything without a usable target or free cells. Icons of apps
  that are not installed are dropped by Launcher3 when it loads the layout.
- Settings are changed only after Launcher3 has accepted the layout, so a failed import
  changes nothing.
- Entry names in the zip are not trusted: only the database and the preference files are
  unpacked, each under a fixed name, with a size limit.

Fix in Launcher3 (WidgetSizeHandler)
- A widget waiting for "tap to set up" has an id Android has reserved but not bound.
  Android 17 answers a size update for such an id with a NullPointerException, which ended
  the launcher whenever the grid reloaded with such a widget on a home screen. Found
  because the import first changed a setting that reloads the grid while the previous
  layout still had unset widgets. The answer is now logged and dropped.

Checked on the Android 17 emulator: the owner's backup itself (two apps of it exist there
and land in their cells), a copy of its layout pointed at apps the emulator has (all 7
icons, 6 folders and 4 widgets in place, one widget set up through Android's dialog), and
two grid reloads with unset widgets on screen.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 10:13:41 +01:00
TMCandClaude Fable 5.2 8aba905d9c Thumb-friendly drawer: compact list, Niagara-style alphabet, dark backdrop; QR code on About
Requests from the owner on 12 November 2026, after using 0.3.1 on the Pixel 9a, with a
photo of Niagara Launcher's alphabet as the reference.

Drawer list
- Rows are 50dp with 34dp icons and 15sp names (were 64dp, 46dp, 17sp); letter headers are
  34dp. A row grows with a large system font so a notification preview still fits.

Alphabet
- Letters sit 20dp apart at most (was 30dp). The strip keeps a stretch of edge as tall as
  26 letters, low on the edge with its end 40dp above the navigation bar, and centres the
  letters that exist in it. It used to spread over the whole edge, which put "A" out of a
  thumb's reach.
- The wave follows Niagara's: letters keep their size, the wave is wide (104dp) so about
  five letters on each side of the finger curve around the thumb, and the selected letter
  is shown large in a light bubble just beyond the wave's peak. This replaces the enlarged
  letter on a dark spot from 0.3.1, which collided with its neighbours at this spacing.
- The list dims a little during any scrub, more where the wave lies over it.

Backdrop
- The drawer's scrim is black at 78% in every theme (AllAppsState). Lawnchair's grey at 40%
  left a light grey behind white names over a light wallpaper. LawnchairScrimView judges the
  status bar icons by the scrim's own colour again, since that colour is now always dark.

Scrub from the home screen (PRD AD-10)
- On by default, the owner's decision; this closes the PRD's open question.
- It opens the drawer in 120 ms instead of the state manager's 600 ms, like swipe up and hold.
- A touch that lifts within 200 ms was a flick and opens the plain list.

Fix: undoing a scrub that was not meant
- 0.3.1 put the list back to "where it was", read from the list's rows. A closed drawer has
  been told to return to the top but lays its rows out only when it opens, so the rows
  still reported the place the drawer was last left at, and a quick swipe reopened it
  there. The list now goes where a plainly opened drawer stands.

About
- A QR code for the release page, generated on the phone with ZXing core (new dependency),
  black on white in every theme, with the address under it.

Checked on the Android 17 emulator with timed touches, a thumb-sized shape over the
screenshots, a 1.6 font scale, and by decoding the QR code from a screenshot.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 09:00:22 +01:00
TMCandClaude Fable 5.2 4e48615b5a Show an app's latest notification under its name in the drawer list
PRD AD-11, the last buildable row of M4.

- FlexeraNotificationPreview keeps the text of the notifications that currently show a
  dot, fed by Launcher3's NotificationListener on its worker thread, and sets a second,
  smaller and dimmer line on a drawer row whenever the row's dot state is applied.
- It uses what the dots already use: the same notification access and the same filter
  for which notifications count. Without notification access nothing changes.
- PopupDataProvider now refreshes an app's icons on every posted notification, so a
  notification updated in place brings its new text to the row.
- Only rows of the drawer list take the second line (marked with a view tag); search
  result icons share the "all apps" display type but stay as they were.
- New setting under Settings > App drawer: Notification previews, on by default.

Checked on the Android 17 emulator (debug build): a test SMS showed under Messages,
a second one replaced it while the drawer was open, removing the notification put the
plain name back, and the setting switches the line off and on.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 00:05:08 +01:00
TMCandClaude Fable 5.2 f92276da15 Ask for the home role after the launcher has finished resuming; keep failed background checks quiet
First run (PRD SI-1)
- The request to become the default home app never appeared. Started from onResume, it
  was still pending when the launcher finished resuming, and Quickstep cancels any
  pending request at that point. It now starts from onDeferredResumed, after that
  check, through Launcher3's own ApiWrapper.assignDefaultHomeRole.

Updater
- An automatic check that fails (phone offline, say) no longer leaves "couldn't reach
  the update server" on the About screen. The screen keeps what it showed before, and
  the check runs again in an hour. A check started by hand still reports the failure.

Checked on the Android 17 emulator with the 0.2.0 release build made before this commit:
swipe up and hold in gesture and in three-button navigation, the list darkening under
a wave that reaches in from the right edge, and the work profile tabs. The two changes
in this commit are not yet checked.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 23:35:14 +01:00
TMCandClaude Fable 5.2 9d444ae2d0 Make releases compile from scratch and start-test them; ask to become the home app on first run
Release script
- The first 0.2.0 build crashed at start with NoSuchFieldError: $stable. Its Kotlin was
  compiled incrementally on top of the 0.1.0 output after a preference class had changed,
  the same fault seen earlier in debug builds. It was caught by hand before publishing.
- flexera/release.ps1 now deletes the release variant's Kotlin output and builds without
  the build cache, so a release never reuses earlier compiled code.
- New step: install the APK on the running emulator, start it, and stop without
  publishing if it crashes or is not running after 25 seconds. Installed copies update
  themselves, so a release that crashes at start must not get out. -NoSmokeTest skips
  the step on a machine without an emulator.

First run (PRD SI-1)
- The first time the launcher comes to the front while another app is the home app, it
  asks Android to offer it as the default home app (FlexeraFirstRun). Asked once; if
  declined, Launcher3's own hint and the reminder in settings remain.

Not yet checked on a device: the first-run prompt.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 23:22:26 +01:00
TMCandClaude Fable 5.2 c9744a1994 Add scrub from home, fix the work profile layout, and let swipe up and hold work with three buttons
Scrub from home (PRD AD-10)
- New setting, off by default: a vertical slide along the alphabet's edge of the home
  screen opens the drawer and scrubs it in the same touch (FlexeraEdgeScrubController).
  It claims a touch only once it has clearly moved up or down, and never during an item
  drag. Nothing is drawn on the home screen.

Work profile (PRD AD-14)
- The Personal and Work tabs and the work info card were one icon wide, because their
  width came from grid maths that assumes several columns. In the one-column list they
  now line up with the search bar.
- The info card's own scroller section no longer shows up as a letter in the alphabet.

Navigation modes (PRD SI-2, AD-17)
- With three-button navigation, the controller that carries swipe up and hold stood aside
  completely, so a hold did nothing there. It now takes the ordinary swipe up from the
  home screen in that mode too.

Wave alphabet
- The list darkens under the wave where the letters cross icons and names: always from
  the left edge, and from the right edge in proportion to how far the wave reaches in.

Release script
- -BuildOnly builds and checks the APK without publishing, so a version can be tried
  first and then published with -SkipBuild.

Checked on the Android 17 emulator (debug build): work tabs at full width, scrub from the
home screen's edge, the search field keeping its background while typing. The
three-button change and the right-edge darkening were written after that build and are
not yet checked.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 23:10:16 +01:00
TMCandClaude Fable 5.2 cdb076bba5 Add the alphabet side setting, screen-reader support and a trimmed drawer settings screen
PRD AD-15 and SI-10, plus polish found while checking the M4 rows on the emulator.

Wave alphabet
- New setting "Alphabet on the left edge" for left-handed use. Rows keep clear of the
  alphabet on whichever edge it sits; a scrub from the left darkens the list under the
  wave, because there the letters cross the icons and names.
- Screen readers see the strip as one slider: it reads the letter the list is at, and the
  adjust gesture moves one letter at a time.
- Letters shrink to fit their slot when a large system font would make them overlap.

Settings
- FlexeraDrawerSettings replaces Lawnchair's App drawer screen, whose grid options
  (columns, row height, icon and label size, drawer folders, scroll bar, panel colour and
  opacity) do nothing in the list drawer or break its white-on-transparent look.
- Settings home and search hint no longer mention column counts or web results.

Search
- The search field keeps its pill while typing; without a drawer panel behind it, the
  dark text sat directly on the wallpaper.

Checked on the Android 17 emulator: drawer search, hiding and restoring an app,
notification dots, swipe down for notifications, the left-edge alphabet and its scrub,
and the slider node in the accessibility tree.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 22:44:43 +01:00
TMCandClaude Fable 5.2 1e49441967 Add self-update from a public release repository
The app is distributed privately, outside any store. Every version is published as a
release with the APK attached in https://git.mariosem.es/msemes/Flexera-Launcher-Releases,
and installed copies update themselves from there.

Versioning
- flexera/version.properties holds the version name (now 0.1.0); build.gradle derives the
  version code from it. This replaces Lawnchair's 16_00_02_01_00 / "16.Dev.(hash)".
- Debug builds always use the debug key, so the release key signs releases only.

Updater (app.flexera.update.FlexeraUpdater)
- Asks the release repository for the latest release at most once a day, when the home
  screen or settings come to the front, and on demand from Settings > About > Updates.
- Downloads the APK into the cache, checks that it is a newer build of this app, and
  hands it to Android's installer. Android enforces the signature.
- Posts a notification when an automatic check finds a newer version.
- Off in debug builds, whose app ID differs from the released one.

Network policy (reverses PRD SI-5, at the owner's request for in-app updates)
- The INTERNET permission is back, plus REQUEST_INSTALL_PACKAGES.
- The update check is the only network use. Lawnchair's online features stay off through
  FlexeraNetwork.onlineFeaturesAllowed: web suggestions, the announcement feed, and the
  crash-report upload button.
- The About screen's privacy text says what the app now does online.

Releasing
- flexera/release.ps1 raises the version, builds the signed APK, checks its signature,
  creates the release and uploads the APK.

Also carries the strings for the next commit (alphabet side and drawer settings).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 22:44:42 +01:00
TMCandClaude Fable 5.2 9f66d06f4d Add the wave alphabet, swipe up and hold, and the transparent fading drawer
PRD M3 (AD-4 to AD-6, AD-17), plus three look changes the owner asked for.

Wave alphabet
- WaveAlphabetView (flexera/src): a strip of letters on the drawer's end edge showing only
  letters that have apps. Touching it jumps the list to that letter with a haptic tick; the
  letters near the finger bulge away from the edge and reach further the further in the
  finger moves; the selected letter is enlarged.
- The strip reserves the edge from the system back gesture and tells the drawer not to
  drag closed under a scrub.
- Launcher3's scroll bar is hidden by default and no longer takes touches while hidden.

Swipe up and hold
- NoButtonNavbarToOverviewTouchController reuses its motion-pause detector on an ordinary
  swipe up from the home screen: a pause finishes opening the drawer and hands the same
  touch to the alphabet. Swipes that start on the navigation bar keep their stock meaning.

Look
- The drawer fades in over the home screen instead of sliding up from the bottom.
- No background panel or drag handle (drawer opacity default 0); the wallpaper keeps
  Lawnchair's blur and scrim.
- App names, letter headers and alphabet letters are white with a soft shadow.

Tested on the Android 17 emulator: quick swipe opens the drawer, edge scrub jumps the
list, swipe up and hold opens the drawer and scrubs from mid-screen, Back closes it.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 22:12:40 +01:00
TMCandClaude Fable 5.2 76c3603693 Turn the app drawer into a one-column list with letter headers
PRD M2 (AD-1 to AD-3, AD-7, AD-8, HS-7). The drawer keeps Launcher3's adapter, sorting,
live updates, click, long-press menu and drag; only what differs from the grid is new.

- FlexeraDrawer (flexera/src): column count, row styling, and a letter-header adapter item
- flexera/res/layout/all_apps_icon.xml overrides Launcher3's grid cell with a horizontal
  row (46dp icon, name beside it, 64dp tall)
- AlphabeticalAppsList adds a header item at the start of each letter group and points
  the fast-scroll section at it; Private Space keeps its own framing
- BaseAllAppsAdapter creates and binds the header view type and styles rows
- DeviceProfileOverrides forces one drawer column
- DeviceProfile.getAllAppsIconStartMargin returns the row padding for a one-column
  drawer, which keeps the search bar full width
- Suggested apps row above the list is off by default

Tested on the Android 17 emulator: scrolling, tap to launch, long-press menu, drag to the
home screen, and live removal and return of an app all work. The right-edge fast
scroller did not respond to scripted drags and still needs a hand test.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 21:25:41 +01:00
TMCandClaude Fable 5.2 9376781e8a Replace Lawnchair's About screen with Flexera Launcher's own
Lawnchair's screen showed its logo, team and community links and loaded contributor
data from the network. FlexeraAbout (flexera/src) shows the app's own icon, name and
version, credits Lawnchair as the project it is built on, states the privacy position,
and keeps the open-source licence list. It makes no network requests.

The About route in PreferenceNavigation now points at it; Lawnchair's About.kt is untouched.

Tested on the Android 17 emulator: the screen renders, the licence list opens, and
no network request or guard message is logged.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:59:06 +01:00
TMCandClaude Fable 5.2 25df1a89e6 Remove the INTERNET permission and guard the network call sites
PRD SI-5: nothing leaves the device.

- flexera/AndroidManifest.xml removes android.permission.INTERNET; it is attached to the
  debug and release build types so it outranks flavor and library manifests
- FlexeraNetwork (new, flexera/src): isAvailable() plus a crash guard, because OkHttp
  rethrows Android's SecurityException on its dispatcher thread and that killed the launcher
- Drawer search skips web suggestions when the permission is missing
- LawnchairApp installs the guard; the bug reporter ignores the same exception

Tested on the Android 17 emulator: typing in the drawer search and opening the About
screen no longer crash; the guard logged and absorbed six refused requests.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 20:48:43 +01:00
caizhixingandcaizhixing 6c592e7924 fix: restart non-default-home Lawnchair with a home-category intent (#7525)
restartLauncher()'s fallback for when Lawnchair isn't the current default home restarts it via PackageManager.getLaunchIntentForPackage(), which builds a CATEGORY_LAUNCHER (standard-typed) intent to the home activity. Some Android 8-10 OEM ROMs react to that launch by trying to re-type the home task and killing the app with RemoteServiceException("Can't change activity type once set: ... activityType=standard") delivered via SCHEDULE_CRASH, which cannot be caught at runtime. Launch the same activity with a home-category intent pinned to our own package instead; the resolved target and flags are unchanged (NEW_TASK was implicit in the launch intent, CLEAR_TOP stays).

Co-authored-by: caizhixing <caizhixing@users.noreply.github.com>
2026-11-11 15:02:37 +08:00
B Vishal ChandrakanthandCopilot ce82ac1248 feat: add tilt to swipe pages for home screen rearrangement and fix app drawer gap (#7500)
* feat and fix : Add Tilt to swipe pages setting

Replace the multi-level Motion controls with a single Home screen toggle named “Tilt to swipe pages,” positioned below Infinite scrolling. Update the description to explain that tilting the phone moves between pages while rearranging apps or widgets, and make the pan controller use the toggle directly while retaining the existing preference key for compatibility. Fix for "[BUG] Large empty gap between app drawer search bar and app grid when app suggestions are disabled
 #7490"

* EDITS : Coderabbit suggested edits for PR #7500

* Coderabbit suggested changes (2) #7500

* Update gradle-daemon-jvm.properties

* Remove unrelated Gradle toolchain changes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add docstrings for pan-to-move and all apps header changes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add a tilt sensitivity control

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-11-09 20:59:50 +08:00
caizhixingandcaizhixing d180feb55f fix: crash when the icon surface view still has a parent on resume (#7513)
LawnchairFloatingSurfaceView.removeViewImmediate() skipped the removal when
isAttachedToWindow was false. onNewIntent is delivered inside
performResumeActivity, while the window can still be re-attaching: the view is
already a child of the drag layer but not attached yet. The removal was
skipped, so the following show() crashed on addView() with

  IllegalStateException: The specified child already has a parent.
  You must call removeView() on the child's parent first.

FloatingSurfaceView removes the view unconditionally, and removeViewInLayout()
is a no-op when the view has no parent, so match it.

Co-authored-by: caizhixing <caizhixing@users.noreply.github.com>
2026-11-09 20:49:49 +08:00
caizhixingandcaizhixing 34d1c3ef88 fix: drop the stale two-pane paged widget sheet overlay (#7514)
lawnchair/res/layout/widgets_two_pane_sheet_paged_view.xml is a leftover copy
of the 2021 two-pane layout that shadows the current one in res/. It is
inflated by WidgetsTwoPaneSheet when a work profile is present, but the AOSP
code has moved on:

- WidgetsFullSheet.setupViews() looks up R.id.search_widgets_list_view, which
  only exists in the current layout. The old copy has no search results list,
  so the lookup returns null and AdapterHolder.setup() crashes on
  setOutlineProvider() while the sheet is being inflated.
- It is also missing R.id.suggestions_header and repeats the "Widgets" title
  that the outer widgets_two_pane_sheet.xml already provides.

Deleting the overlay lets the AOSP layout in res/ win, matching the
recyclerview variant which was already realigned with AOSP in #7188.

Co-authored-by: caizhixing <caizhixing@users.noreply.github.com>
2026-11-09 16:59:11 +07:00
x-JOCandClaude Opus 5.5 5152a54bc0 perf(startup): install the bundled baseline profile on sideloaded builds (#7412)
* fix(startup): install the shipped baseline profile on sideloaded builds

Removing androidx.startup's InitializationProvider (AOSP b/197780098)
also removed ProfileInstallerInitializer, so GitHub and Nightly APKs
never wrote the bundled baseline profile and ran unoptimised until
background dexopt built a profile of its own.

Do the initializer's job from LawnchairApp instead: main process only,
5 s after start, on the background-priority ordered executor. The
provider stays removed, so no other library initializes eagerly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(startup): only install the profile from the main process on API 26-27

Below API 28 the version check let every process through, including
:bugReport. Look up this pid's process name with ActivityManager there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 19:12:50 +07:00
Lawnchair BotandCrowdin Bot 677e9c4aa3 New Crowdin translations by GitHub Action (#7507)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-11-08 00:21:47 +00:00
SEED264andClaude Opus 5.5 aeb92c164b fix(bugreport): prevent the launcher from hanging after a crash (#7499)
The bug report file name used the locale-dependent
SimpleDateFormat.getDateTimeInstance(), which yields '/' in some
locales (e.g. ja_JP: "2026/09/28 12:34:56"). The resulting path had
non-existent parent directories, so createNewFile() threw inside the
uncaught exception handler and the default handler was never invoked,
leaving the process frozen instead of restarting.

- Use a fixed, locale-independent format (yyyy-MM-dd_HH-mm-ss, Locale.US)
- Always delegate to the default handler via try/finally so the process
  is killed even if generating the report fails

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-08 00:29:32 +08:00
8801864504 fix: crash in icon cache when a component disappears during package update (#7481)
On Android 11 and below, LawnchairActivityCachingLogic#loadIcon resolves
ActivityInfo through PackageManager#getActivityInfo because
LauncherActivityInfo#getActivityInfo is only public since Android 12. When a
package is updated while the launcher refreshes its icon cache, one of its
launcher activities can vanish between LauncherApps handing out the activity
list and this lookup. The resulting NameNotFoundException is uncaught and
kills the model thread:

Fatal Exception: android.content.pm.PackageManager$NameNotFoundException
       at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:471)
       at app.lawnchair.LawnchairActivityCachingLogic.loadIcon(LawnchairActivityCachingLogic.kt:60)
       at com.android.launcher3.icons.cache.BaseIconCache.addIconToDBAndMemCache(BaseIconCache.kt:235)
       at com.android.launcher3.icons.IconCache.updateIconsForPkg(IconCache.java:206)
       at com.android.launcher3.model.PackageUpdatedTask.execute(PackageUpdatedTask.java:160)
       at com.android.launcher3.LauncherModel.enqueueModelUpdateTask(LauncherModel.kt:412)

Catch the exception and return BaseIconCache#getDefaultIcon instead:
addIconToDBAndMemCache already treats the default icon as "could not be
loaded" and skips the DB/memory cache entry, matching how other icon load
failures are handled. The two other PackageManager#getActivityInfo call
sites (ThemedIconCompat, SearchResultIcon) already catch this exception; the
CachingLogic was the only unguarded one.

Android 12+ is unaffected: it reads the ActivityInfo snapshot from
LauncherActivityInfo directly.

Co-authored-by: caizhixing <17589399+caizhixing03@user.noreply.gitee.com>
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
2026-11-05 15:20:18 +00:00
Pun Butrach 849fec9ef6 feat: QoL improvement to suspend application usage (#7338)
* fix: Tint suspend icon

* feat: Check if icon is suspendable

* fix: Resolve app name correctly

* fix: Improve permission detection

* chore: Please the spotless lord

* fix: Apply suggestions

* chore: Apply suggestions

* chore: Apply suggestions
2026-11-01 15:08:31 +07:00
stevenandsteven-ahfu f6ee1d9b3c fix: Handle missing themed icon drawables (#7466)
Co-authored-by: steven-ahfu <4956188+steven-ahfu@users.noreply.github.com>
2026-11-01 13:46:07 +07:00
Lawnchair BotandCrowdin Bot f84b9e3564 New Crowdin translations by GitHub Action (#7470)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-11-01 00:21:50 +00:00
steven c154f993c8 [Fix]: Stop platform from overriding Lawnchair's own dark theme (#7267) 2026-10-30 14:43:23 +00:00
Pun Butrach be56ee534f refactor: Move Lawnchair gesture vibrator to MSDL player (#7464)
* feat: Move gesture vibrator to MSDL player

* chore: Please the lord Spotless
2026-10-30 21:32:40 +07:00
Pun Butrach f9b99e2da2 fix: Icon not responding to system accent changes (#7370) 2026-10-30 16:33:00 +07:00
Pun Butrach 15ebe41583 refactor: Hide battery charging speeds behind code flag (#7449) 2026-10-27 16:57:10 +00:00
Pun Butrach 7436e7408d refactor: Pass swipe down gesture notifications panel to native touch controller (#7448)
* refactor: Pass swipe down gesture over to native touch controller

* chore: Spotlessified

* fix: Apply reviewer
2026-10-27 22:34:29 +07:00
Pun Butrach 586cb6d5ba refactor: Adapt Lawnchair Shape function to PathWrapper 2026-10-26 00:15:33 +07:00
Lawnchair BotandCrowdin Bot 8be1c0f469 New Crowdin translations by GitHub Action (#7438)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-10-25 03:57:47 +03:30
def 793ac98db2 feat(feed): add Whisper to the feed provider whitelist (#7399) 2026-10-25 01:18:32 +08:00
Pun Butrach eba9492be1 Revert "feat(home): live glass blur behind icons, folders and widgets (#7393)" (#7419)
This reverts commit d9afe890a3.
2026-10-21 22:19:44 +07:00
x-JOCandClaude Opus 5.5 d04b3b1c8d fix(prefs): reload the grid with the value just set (#7404)
* fix(prefs): reload the grid with the value just set

Most grid preferences reload the grid in their onSet. onSet runs right
after the DataStore write, but the device profiles read the preferences
through PreferenceManager2's cache (firstCached), which is updated by its
own collector on the main thread. That collector may not have run yet
when the reload runs inline, so the grid could be rebuilt from the
previous value: the change shows up only on the next reload, for example
after the next change of any grid preference.

ReloadHelper.reloadGrid now refreshes the cache from the DataStore, which
already has the new value, before reloading the grid. This fixes every
preference whose onSet calls reloadGrid, and the other callers
(restart(), the folder settings), with no change at the call sites. The
reload now always runs on the main thread, through PreferenceManager2's
Main.immediate scope.

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit cbe549ade0bac9d655a965ed8f802c8c19573ef5)

* fix(prefs): keep a newer cached value when the fresh read finishes late

With two quick grid changes, the first refresh could finish after the
collector had already stored the second value and put the older snapshot
back. Store the read result only if the cache hasn't changed meanwhile.

Suggested in review by CodeRabbit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 19:44:10 +08:00
R05P0 d9afe890a3 feat(home): live glass blur behind icons, folders and widgets (#7393)
* feat(home): live glass blur behind icons, folders and widgets

Add an optional "Glass blur" setting that blurs the wallpaper behind home
screen icons, folder icons, open folders and widgets, like CSS
backdrop-filter. SurfaceFlinger renders the blur through blur regions
(a copy of the framework BackgroundBlurDrawable), so it stays live and
follows the items during page scrolls and animations.

SurfaceFlinger ignores all the blur of an app layer once there are more
than 10 blur requests on screen (kMaxBlurRequests, b/399120953), unless
the layer was created by a privileged process. With root, the regions go
to an effect layer created by the root helper and placed below the
launcher window content. Without root, they go to the window layer and
are subject to that limit.

Also fix RootHelperManager, unused until now: it reported root as not
available until a root shell had been created, and bound the root
service off the main thread.

* fix(home): redraw when a glass blur region is removed

Without a frame the pre-draw listener never sends the regions left, so a
removed blur, the last one in particular, could stay on screen until the
next redraw.
2026-10-21 19:34:15 +08:00
x-JOCandClaude Opus 5.5 3974765ea9 fix(drawer): update the search bar padding when the device profile changes (#7405)
AllAppsSearchInput sets its horizontal padding from the device profile
only when it is inflated and when the IDP changes. A DeviceProfile change
without an IDP change (rotation, or folding and unfolding once the
device type is stable) left the padding of the old profile. When it did
run, setupPadding() also reset the full padding even while a search was
active, where the bar uses half of it, and the focus and text listeners
kept animating to the paddings captured at inflation.

- Listen to DeviceProfile changes and update the padding then too.
- Keep the half padding while the search box is focused or holds a
  query, and cancel a running padding animation first.
- Animate to the current paddings rather than the inflation ones.

The search text and results are untouched. The result views already
follow the new profile: ActivityAllAppsContainerView#onDeviceProfileChanged
swaps the adapters and clears the view pool of every list, the search
list included, so all result views are inflated again with it.


(cherry picked from commit 090ce597ed7f6e35ee7fecb8235ea52d7be155c7)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 17:12:00 +08:00
x-JOCandClaude 5c1979227e fix(fold): keep the device type stable across folds (#7402)
* fix(display): keep the device type stable across folds

LawnchairWindowManagerProxy estimated the supported bounds only from
DisplayManager#getDisplays, which reports the panel in use. On a foldable
the DisplayController cache then held a single state, so every fold and
unfold invalidated it ("Unexpected normalizedDisplayInfo found"), the
device type flipped between PHONE (folded) and MULTI_DISPLAY (unfolded,
through the hinge check), and CHANGE_SUPPORTED_BOUNDS reloaded the grid
and the model. The smaller dock of the phone profile also made the
loader delete the dock items past its size.

On devices with FEATURE_SENSOR_HINGE_ANGLE, the estimate now also holds
the other states of the internal display:
- WindowManager#getPossibleMaximumWindowMetrics, like
  SystemWindowManagerProxy, when the system answers (it only does for the
  recents provider) and the current state is one of its states.
- Otherwise each state of the default display seen on the device is
  remembered. A state with the same aspect ratio replaces an older one,
  so a resolution change leaves no stale bounds.

The extra states are built the way DisplayController.Info builds the key
it looks up (getDisplayInfo(context).normalize(), no-cutout default), so
the lookup finds them and folding no longer changes the supported
bounds. Once both panels are known they hold a phone and a tablet size,
and the device type is MULTI_DISPLAY in both states, across restarts too.
A hinge device never seen unfolded still reports PHONE: flip phones also
have the hinge sensor, so it can't be assumed.

The states are kept in their own device-protected file,
lc_display_states, not in boot_aware_prefs: DisplayController can run
before unlock, the values must not be backed up or restored on another
device, and the write happens while DisplayController is being
constructed. AOSP's taskbar-pinning listener on boot_aware_prefs calls
getInfo() for every change, which is still null at that point, so a write
to that file crashed the launcher when it started on the other screen
than last time.

The hinge check now lives in one place, LawnchairWindowManagerProxy
.isFoldable(), which DisplayController.Info uses as well.

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 14e6051ac83cba92cb371eec1c408966cb41ba9d)

* chore(display): log the device type once per info

getDeviceType() is called many times for each display change (once per
DeviceProfile and more), and logged its result every time. Log it once
per DisplayController.Info, that is once per display change, with the
current size, so the device type of each state can be read in logcat.

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 9fa57ec7e3d678c3e19d6ce1309ae11b893e18fc)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 17:09:10 +08:00
x-JOCandClaude Opus 5.5 d6f0bbcea9 fix(search): stop double-encoding web suggestion queries (#7389)
Retrofit already percent-encodes @Query parameters, so encoding the query with
Uri.encode first sent the literal "%D8%A7..." text to the suggestion APIs.
Non-ASCII queries (Arabic, Cyrillic, accented Latin, ...) got suggestions for
that escaped text, shown as hex bytes. Pass the raw query instead. The search
URLs built by hand in getSearchUrl still encode, as they should.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:58:07 +08:00
x-JOCandClaude Opus 5.5 75515f2c1b fix(i18n): plurals for relative times, two hardcoded strings (#7411)
* fix(about): count the changes' relative times as plurals

"5m", "3h" and the other relative times in the changes dialog were plain
strings, so languages that change the noun with the number, like
Arabic ("دقيقة", "دقيقتين", "5 دقائق"), had to pick one form that is
wrong for most numbers. Make them plurals; English stays the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(folders): translatable "Manage apps" in the drawer folder editor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(settings): translatable empty pane text on large screens

The pane next to the settings menu on tablets and unfolded phones said
"Tap a preference in the left hand menu" in English only, and the menu
is on the right in right-to-left languages. Use a string that names no
side.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:44:15 +08:00
TheSuperDragon fc100ae909 refactor: Improve icon system UX and consistency (#7397)
This commit improves Lawnchair's icon system by reorganizing the preference screens, while making icon behavior more deterministic in `LawnchairIconProvider` and cleaning up old and unused preferences.
2026-10-19 00:32:14 +08:00
Lawnchair BotandCrowdin Bot 6b426467a0 New Crowdin translations by GitHub Action (#7396)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-10-18 03:58:46 +03:30
Joseph Petersen 85335d904a fix(dock): remove unused label space when labels are hidden (#7364)
* fix(dock): remove unused label space when labels are hidden

* test(dock): remove tests and setup per review
2026-10-14 00:50:16 +08:00
Lawnchair BotandCrowdin Bot 62a50ddfa9 New Crowdin translations by GitHub Action (#7374)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-10-14 00:46:17 +08:00
Diego Pérez RuanovaandDiego Pérez Ruanova 7e082d84eb fix: let the paged dock keep mostly horizontal swipes (#7381)
VerticalSwipeTouchController uses BothAxesSwipeDetector, which has no angle
check. Once the touch drifts vertically by more than the touch slop it takes
over the stream and the dock pager receives ACTION_CANCEL, so a normal thumb
swipe (e.g. 450 px horizontal, 90 px vertical) leaves the dock half scrolled
or snaps back to the starting page.

When the touch starts on a paged dock, stop offering it to the vertical swipe
detector as soon as the motion is mostly horizontal (|dy| < 2 * |dx|).

- Closes: #7149

Co-authored-by: Diego Pérez Ruanova <25953869+dperezruanova@users.noreply.github.com>
2026-10-13 13:40:47 +08:00
Pun Butrach 4793ba3a23 fix: Apply scaling to icon shape preview for consistency (#7369) 2026-10-06 22:04:37 +07:00