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>
Three more requests from the owner on 12 November 2026.
Search field
- The field scrolls away with the list and comes back at the list's top, so the list has
the whole height once it is scrolled. The field is not turned into a list row. The list
now starts at the top of the drawer instead of under the field and keeps the field's room
as top padding (ActivityAllAppsContainerView.replaceAppsRVContainer and
AdapterHolder.applyPadding); FlexeraDrawer.moveSearchFieldWithList shifts the field by as
much as the list has scrolled, measured from the first row, and cuts it at the same line
the rows are cut at, the lower edge of the status bar.
- With work tabs, a hidden or a floating search field, and during a search, nothing changes.
Alphabet
- A magnifier above the letters stands for the search field. Scrubbing to it jumps the list
to its top; the bubble shows the magnifier. The stretch of edge kept for the strip grows
by one place for it.
- The strip measures its room from where the list's rows start, not from the list's top.
Chosen letter
- A scrub brings the letter's group to the middle of the screen, where the thumb is,
instead of to the top: the group's middle on the list's middle, a long group's header no
higher than 22% of the height. At either end of the list the list stops at its end, so
the first groups stay at the top and the last ones rest at the bottom.
Checked on the Android 17 emulator: scrolling by hand, scrubs to a middle letter, the last
letter and the magnifier, typing a search and leaving it, reopening after a scrub, the hold
and edge gestures, and a row's long-press menu.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
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>
Gradle prints compiler warnings on its error stream. When the script's output was piped or
sent to a file, PowerShell 5.1 took the first such line for an error and stopped the build
at step 2 (seen on the first 0.3.1 attempt). The streams are now merged inside cmd, and
only the exit code decides whether the build failed.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Two reports from first use on the Pixel 9a (PRD AD-5, AD-6, AD-17).
Swipe up and hold was slow to hand over to the alphabet:
- After the hold was noticed, the drawer was sent to its open state through the state
manager, which takes 600 ms, and the alphabet took the finger only after that, and then
only on the next touch movement. A finger held perfectly still never started it.
- Now the alphabet takes the finger's position in the same call that notices the hold, and
the drawer finishes opening under it by playing the swipe's own animation to its end in
120 ms.
- A still finger counts as holding after 150 ms instead of 300 (MotionPauseDetector gains
setForcePauseTimeout, reset by clear()), and the swipe has to travel 32dp instead of 72dp
before a hold counts.
- Because the list now jumps at once, a finger that lifts within 200 ms of the hold being
noticed is treated as an ordinary swipe that slowed down at its end: the scrub is undone
and the list stays where it was.
The selected letter sat under the thumb:
- The wave's peak was 52dp beyond the finger, about 8 mm, and a thumb hides about 11 mm to
each side of where it touches. It is now 104dp, and at least 96dp out from the edge.
- The peak may travel to within 36dp of the far edge (was 70% of the width), so the gap
holds when the finger is in the middle of the screen.
- A taller wave is wider (a third of its reach), so the neighbouring letters bend around
the thumb instead of vanishing under it.
- A soft dark spot behind the selected letter keeps it readable where it lands on an app
name or a light wallpaper.
Checked on the Android 17 emulator with touches injected at millisecond timing (a monkey
script) and a thumb-sized shape drawn over the screenshots.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- App ID com.sema.flexeralauncher (debug builds add .debug)
- App name Flexera Launcher and an adaptive icon built from the artwork in flexera/branding
- New overlay folder flexera/res, wired into the lawnWithQuickstepGithub source set,
so branding and defaults override Lawnchair's without editing its files
- Defaults switched off: At a Glance, Lawnchair's announcement feed, nightly updater
- English strings that named Lawnchair now say Flexera Launcher
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
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>
* 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>
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>
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>
* 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>
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>
* fix: don't crash at startup when the restriction bypass lib is missing
Unseal.unseal() loads ChickenHook's libnrb.so. Repackaged APKs and
multi-open containers can fail to load it, in which case the call throws
UnsatisfiedLinkError (an Error) that catch (Exception) does not handle. This
runs from Application.onCreate(), so the process crash-loops forever.
Catch Exception | LinkageError. A failed unseal only disables the hidden API
exemption for that launch; other Errors (OOM, etc.) still propagate.
* fix: don't crash reading allowEnterPip when the restriction bypass is broken
getAllowEnterPip() uses RestrictionBypass below SDK 35. If the native bypass
library failed to load, the bypass class is permanently broken and any further
access throws LinkageError (ExceptionInInitializerError on the first touch,
NoClassDefFoundError afterwards), which catch (ReflectiveOperationException)
does not handle.
Return false (PIP not allowed) instead of crashing the transition.
---------
Co-authored-by: caizhixing <17589399+caizhixing03@user.noreply.gitee.com>
mSettingsCache is only assigned in onListenerConnected(), but
onListenerDisconnected() unregisters from it unconditionally. The framework
calls onListenerDisconnected() from NotificationListenerService.onDestroy(),
so a service that is destroyed before it ever connects dereferences null and
kills the process.
Guard the unregister; the connected path is unchanged.
Co-authored-by: caizhixing <17589399+caizhixing03@user.noreply.gitee.com>
getAnimationData() read itemsInPreview[0] to derive the base icon size. The
preview list can be empty when the folder has no bound child views yet, which
made opening the folder crash with an IndexOutOfBoundsException.
Fall back to the device profile icon size when the list is empty; the normal
path is unchanged.
Co-authored-by: caizhixing <17589399+caizhixing03@user.noreply.gitee.com>
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>
* fix(widgets): let the resize frame reach the screen edge horizontally
The resize frame is drawn resize_frame_background_padding (24dp) outside
the widget, but the horizontal drag range clamped the frame's outer edge
to the DragLayer. When the grid sits close to the screen edge (low
workspace/widget padding, many columns) the last column could not be
reached.
When the frame already overhung the screen edge (gap + widget padding
< 24dp, which is the default), the old range did not contain 0 (left
start positive, right end negative), so the first move event snapped the
frame up to 24dp inward on either side. With half-cell resize steps that
would be enough to drop a step.
Bound the widget's edge instead of the frame's, and keep 0 inside the
range so a touch without movement never moves the frame. The vertical
range is unchanged.
* fix(widgets): count the last partial resize step when the finger hits the edge
The finger cannot travel past the screen edge, and the handle can be
grabbed up to 24dp outside the widget. With little workspace/widget
padding, the travel left for the last column can then be shorter than
RESIZE_THRESHOLD of a step.
While a left/right handle is dragged and the finger is within
resize_frame_background_padding of that screen edge, a partial step of
more than 1 - RESIZE_THRESHOLD towards the edge counts as reached. That
threshold keeps the step from being undone right after it is taken, and
it only applies in the growth direction.
* refactor(widgets): shorten resize frame LC-Notes
Comment-only change: trim the edge-clamp LC-Notes in AppWidgetResizeFrame to one or two lines. No behaviour change.
createDatabaseHelper renamed restored.db to the IDP's dbFile but opened
the dbFile it was passed, from the DB_FILE pref. The Nova import writes
grid prefs before restoring, and hotseatColumns' reloadGrid listener can
move the IDP to an intermediate grid (e.g. launcher_6_4_5.db) first. The
restored layout then landed in a file that was never opened, the opened
file was created empty, performRestore sanitized nothing, and the next
start reset the "empty" database to the default layout. A second import
worked because the prefs no longer changed.
Rename into the file that is opened instead.
migrateDbName moves launcher.db to dbName when dbName doesn't exist yet.
With the restore now renamed into dbFile, a dbFile of launcher.db (the
5_by_5 grid) would be moved away and deleted before DatabaseHelper opens
it. Migrate first, then rename.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>