Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Hiding an app from the drawer deletes the entire home screen folder containing it

オープン
#423 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
70/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
android, kotlin
領域
mobile

調査の方向性

Start with MainActivity.kt, reading refreshLaunchers(), getAllAppLaunchers(), and hideIcon() to understand how hidden apps are distinguished from uninstalled apps. Then inspect HomeScreenGridItemsDao.kt and its deleteByPackageName() behavior. Done means hiding an app no longer removes its folder or the folder's other contents; verify by following the reproduction steps in the issue.

索引モデルが issue の本文から書いたものです。

説明

bug needs triage
Checklist
  • I can reproduce the bug with the latest version given here.
  • I made sure that there are no existing issues - open or closed - to which I could contribute my information.
  • I made sure that there are no existing discussions - open or closed - to which I could contribute my information.
  • I have read the FAQs inside the app (Menu -> About -> FAQs) and my problem isn't listed.
  • I have taken the time to fill in all the required details. I understand that the bug report will be dismissed otherwise.
  • This issue contains only one bug.
  • I have read and understood the contribution guidelines.
Affected app version

1.10.0 (Latest)

Affected Android/Custom ROM version

Android 13

Affected device model

Xiaomi Redmi Note 10 Pro

How did you install the app?

Google Play Store

Steps to reproduce the bug
  1. Open Fossify Launcher and place 2 or more app icons on the home screen
  2. Drag one app icon onto another to create a folder, then drag the remaining app(s) into the same folder — the folder should now contain multiple apps.
  3. Open the App Drawer (swipe up).
  4. Long-press on one of the apps that is inside the folder (e.g. Music) and select "Hide".
  5. Return to the home screen.
Expected behavior
  • The hidden app should be removed from the folder.
  • The remaining apps in the folder should stay in the folder.
  • The folder should remain visible on the home screen with its remaining contents intact.
Actual behavior
  • The entire folder disappears from the home screen, including all other apps that were inside it.
  • All apps that were in the folder are completely removed from the home screen.
  • The hidden app correctly appears in Settings → "Manage hidden icons", but the collateral damage to the folder and its other contents is permanent.
Screenshots/Screen recordings
1. Apps on the home screen 2. Apps grouped into a folder
(Screenshot showing Music, Gallery, File Manager icons side by side on the home screen) ignoreImageMinify (Screenshot showing the 3 apps inside a single folder) ignoreImageMinify
3. Hiding one app from the drawer 4. Folder has disappeared
(Screenshot showing the long-press context menu with "Hide" selected on one of the apps)
ignoreImageMinify
(Screenshot showing the home screen where the folder previously was — now empty)
ignoreImageMinify
Additional information
Root Cause Analysis
The Bug Chain

The issue is caused by the refreshLaunchers() method in MainActivity mistakenly treating a hidden app as an uninstalled app, which triggers a destructive database cleanup that cascade-deletes the folder and all its contents.

Here is the exact execution chain:

Step 1 — User hides the app

When the user selects "Hide" from the app drawer context menu, MainActivity.hideIcon() (MainActivity.kt:882–891) executes:

private fun hideIcon(item: HomeScreenGridItem) {
    ensureBackgroundThread {
        val hiddenIcon = HiddenIcon(null, item.packageName, item.activityName, item.title, null)
        hiddenIconsDB.insert(hiddenIcon)

        runOnUiThread {
            binding.allAppsFragment.root.onIconHidden(item)
        }
    }
}

This method only:

  1. Inserts a HiddenIcon record into the hidden_icons database table.
  2. Removes the app from the drawer UI (AllAppsFragment.onIconHidden()).

It does NOT:

  • Remove the app's icon from any home screen folder.
  • Clean up or update home_screen_grid_items for the hidden app.
  • Update IconCache.launchers to reflect the hiding.
Step 2 — refreshLaunchers() treats the hidden app as uninstalled

On onResume(), refreshLaunchers() (MainActivity.kt:596–619) runs:

private fun refreshLaunchers() {
    val launchers = getAllAppLaunchers()  // Filters out hidden apps!
    // ...

    IconCache.launchers.map { it.packageName }.forEach { packageName ->
        if (!launchers.map { it.packageName }.contains(packageName)) {
            launchersDB.deleteApp(packageName)
            homeScreenGridItemsDB.deleteByPackageName(packageName)  // DESTRUCTIVE!
        }
    }

    IconCache.launchers = launchers
    // ...
}

getAllAppLaunchers() (MainActivity.kt:1082–1131) queries installed apps and filters out hidden ones:

if (hiddenIcons.contains("$packageName/$activityName")) {
    continue  // Hidden app excluded from result
}

The comparison loop at line 601–605 then finds the hidden app's package in IconCache.launchers (old cache, still containing the hidden app) but NOT in the new filtered launchers list. It concludes the app has been uninstalled and calls homeScreenGridItemsDB.deleteByPackageName(packageName).

Step 3 — deleteByPackageName cascade-deletes the folder

HomeScreenGridItemsDao.deleteByPackageName() (HomeScreenGridItemsDao.kt:56–60):

@Transaction
fun deleteByPackageName(packageName: String) {
    deleteItemByPackageName(packageName)         // (1)
    deleteItemsByParentPackageName(packageName)   // (2)
}

Query (1) — deleteItemByPackageName:

DELETE FROM home_screen_grid_items WHERE package_name = :packageName

This deletes ALL grid items (icons, shortcuts, AND the folder itself) whose package_name matches.

Critical detail: When a folder is created by dragging App A onto App B, the folder entity inherits App B's packageName (see HomeScreenGrid.kt:734: potentialParent.copy(type = ITEM_TYPE_FOLDER, ...)). So if the hidden app's packageName matches the folder's packageName, the folder itself is deleted.

Query (2) — deleteItemsByParentPackageName:

DELETE FROM home_screen_grid_items
WHERE parent_id IN (
    SELECT id FROM home_screen_grid_items WHERE package_name = :packageName
)

This deletes ALL children of any item (folder) whose packageName matches. Since the folder's packageName may match the hidden app, all remaining items in the folder are also deleted.

Step 4 — fetchGridItems() redraws without the folder

After the cleanup, fetchGridItems() reloads from the database. The folder and its children are gone from the DB, so they no longer appear on the home screen.

Summary
User hides app → hideIcon() only adds to hidden_icons DB
    → onResume() → refreshLaunchers()
        → getAllAppLaunchers() excludes hidden app
        → Loop compares old cache vs new list → "missing" package detected
        → deleteByPackageName() runs → deletes folder + all children from DB
    → fetchGridItems() → folder is gone from home screen

The fundamental mistake is that refreshLaunchers() cannot distinguish between:

  • An app that was uninstalled (should clean up home screen items)
  • An app that was hidden (should NOT touch home screen items)
Suggested Fix

The fix should address the core issue: refreshLaunchers() should not treat hidden apps as uninstalled apps. There are multiple approaches:

Approach 1: Exclude hidden packages from the cleanup comparison (Recommended)

In refreshLaunchers(), add hidden packages to the known packages set so they are not treated as uninstalled:

private fun refreshLaunchers() {
    val launchers = getAllAppLaunchers()
    binding.allAppsFragment.root.gotLaunchers(launchers)
    binding.widgetsFragment.root.getAppWidgets()

    val hiddenPackages = hiddenIconsDB.getHiddenIcons().map { it.packageName }.toSet()
    val knownPackages = launchers.map { it.packageName }.toSet() + hiddenPackages

    IconCache.launchers.map { it.packageName }.forEach { packageName ->
        if (!knownPackages.contains(packageName)) {
            launchersDB.deleteApp(packageName)
            homeScreenGridItemsDB.deleteByPackageName(packageName)
        }
    }

    IconCache.launchers = launchers
    // ...
}
Approach 2: Also remove the hidden icon from the folder in hideIcon()

In addition to Approach 1, hideIcon() should also handle removing the app from any home screen folder:

private fun hideIcon(item: HomeScreenGridItem) {
    ensureBackgroundThread {
        val hiddenIcon = HiddenIcon(null, item.packageName, item.activityName, item.title, null)
        hiddenIconsDB.insert(hiddenIcon)

        // Remove the hidden app's icon(s) from any home screen folders
        val gridItems = homeScreenGridItemsDB.getAllItems()
        gridItems.filter {
            it.packageName == item.packageName
            && it.activityName == item.activityName
            && it.parentId != null
        }.forEach { folderItem ->
            homeScreenGridItemsDB.deleteItemById(folderItem.id!!)
            homeScreenGridItemsDB.shiftFolderItems(
                folderId = folderItem.parentId!!,
                shiftFrom = folderItem.left,
                shiftBy = -1
            )
            // Clean up empty folders
            val remainingItems = homeScreenGridItemsDB.getFolderItems(folderItem.parentId!!)
            if (remainingItems.isEmpty()) {
                homeScreenGridItemsDB.deleteItemById(folderItem.parentId!!)
            }
        }

        runOnUiThread {
            binding.allAppsFragment.root.onIconHidden(item)
            binding.homeScreenGrid.root.fetchGridItems()
        }
    }
}
Approach 3: Fix deleteByPackageName to be folder-aware

Modify deleteByPackageName so it does not delete folder entities or their children indiscriminately. Instead, it should only delete individual items and properly handle folder cleanup (remove from folder, shift indices, delete folder if empty).

Recommendation: Approach 1 is the minimal and safest fix. It directly addresses the root cause without refactoring the folder management logic. Approach 2 can be applied as an enhancement for a more complete UX where hiding an app also removes its icon from folders.

Related Files
File Relevance Key Lines
MainActivity.kt hideIcon() method, refreshLaunchers() method, onResume() lifecycle, getAllAppLaunchers() L269–289, L596–619, L882–891, L1082–1131
HomeScreenGridItemsDao.kt deleteByPackageName() cascade transaction, deleteItemByPackageName, deleteItemsByParentPackageName L44–48, L56–60
HomeScreenGrid.kt fetchGridItems(), folder creation logic (potentialParent.copy), removeItemFromHomeScreen(), HomeScreenFolder.getItems() L236–261, L290–351, L733–761, L1941–1980
AllAppsFragment.kt onIconHidden() drawer update, onAppLauncherLongPressed() L160–176, L221–246
Activity.kt handleGridItemPopupMenu() — "Hide" option visibility logic L106–107, L160
HiddenIcon.kt HiddenIcon data model L6–18
HomeScreenGridItem.kt Grid item entity — parentId, packageName, type, FOLDER_MAX_CAPACITY L12–41
主要言語
Kotlin
スター
703
フォーク
85
平均マージ
2日 22時間
マージ済み PR(30日)
10

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

FossifyOrg/Launcher のほかの issue

FossifyOrg/Launcher の issue をすべて見る

似ている issue

Kotlin の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。