Kaynağa Gözat

Forked from unlauncher, a fork of slim launcher, for feature addition and LTS. added folders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
User 3 hafta önce
ebeveyn
işleme
1e69e1d957
92 değiştirilmiş dosya ile 1633 ekleme ve 637 silme
  1. 5 3
      app/build.gradle.kts
  2. 0 183
      app/src/main/java/com/sduduzog/slimlauncher/adapters/AppDrawerAdapter.kt
  3. 0 49
      app/src/main/java/com/sduduzog/slimlauncher/ui/options/CustomizeAppDrawerAppListFragment.kt
  4. 0 3
      app/src/main/java/com/sduduzog/slimlauncher/utils/ISubscriber.kt
  5. 0 7
      app/src/main/java/com/sduduzog/slimlauncher/utils/OnAppClickedListener.kt
  6. 1 1
      app/src/main/java/com/simplauncher/App.kt
  7. 2 2
      app/src/main/java/com/simplauncher/MainActivity.kt
  8. 4 4
      app/src/main/java/com/simplauncher/adapters/AddAppAdapter.kt
  9. 280 0
      app/src/main/java/com/simplauncher/adapters/AppDrawerAdapter.kt
  10. 5 5
      app/src/main/java/com/simplauncher/adapters/CustomAppsAdapter.kt
  11. 4 4
      app/src/main/java/com/simplauncher/adapters/CustomizeAppDrawerAppsAdapter.kt
  12. 48 0
      app/src/main/java/com/simplauncher/adapters/CustomizeFolderAppsAdapter.kt
  13. 41 0
      app/src/main/java/com/simplauncher/adapters/CustomizeFoldersAdapter.kt
  14. 6 6
      app/src/main/java/com/simplauncher/adapters/HomeAdapter.kt
  15. 2 2
      app/src/main/java/com/simplauncher/data/BaseDao.kt
  16. 2 2
      app/src/main/java/com/simplauncher/data/BaseDatabase.kt
  17. 2 2
      app/src/main/java/com/simplauncher/data/model/App.kt
  18. 23 13
      app/src/main/java/com/simplauncher/datasource/UnlauncherDataSource.kt
  19. 2 2
      app/src/main/java/com/simplauncher/datasource/apps/UnlauncherAppsMigrations.kt
  20. 5 5
      app/src/main/java/com/simplauncher/datasource/apps/UnlauncherAppsRepository.kt
  21. 2 2
      app/src/main/java/com/simplauncher/datasource/apps/UnlauncherAppsSerializer.kt
  22. 3 3
      app/src/main/java/com/simplauncher/datasource/coreprefs/CorePreferencesMigrations.kt
  23. 5 5
      app/src/main/java/com/simplauncher/datasource/coreprefs/CorePreferencesRepository.kt
  24. 2 2
      app/src/main/java/com/simplauncher/datasource/coreprefs/CorePreferencesSerializer.kt
  25. 125 0
      app/src/main/java/com/simplauncher/datasource/folders/AppDrawerFoldersRepository.kt
  26. 25 0
      app/src/main/java/com/simplauncher/datasource/folders/AppDrawerFoldersSerializer.kt
  27. 2 2
      app/src/main/java/com/simplauncher/datasource/quickbuttonprefs/QuickButtonPreferencesMigrations.kt
  28. 3 3
      app/src/main/java/com/simplauncher/datasource/quickbuttonprefs/QuickButtonPreferencesRepository.kt
  29. 2 2
      app/src/main/java/com/simplauncher/datasource/quickbuttonprefs/QuickButtonPreferencesSerializer.kt
  30. 2 2
      app/src/main/java/com/simplauncher/di/ActivityModule.kt
  31. 3 3
      app/src/main/java/com/simplauncher/di/AppModule.kt
  32. 3 3
      app/src/main/java/com/simplauncher/di/RepositoryModule.kt
  33. 2 2
      app/src/main/java/com/simplauncher/models/AddAppViewModel.kt
  34. 1 1
      app/src/main/java/com/simplauncher/models/CustomiseAppsViewModel.kt
  35. 2 2
      app/src/main/java/com/simplauncher/models/HomeApp.kt
  36. 2 2
      app/src/main/java/com/simplauncher/models/MainViewModel.kt
  37. 2 2
      app/src/main/java/com/simplauncher/models/Repository.kt
  38. 2 2
      app/src/main/java/com/simplauncher/ui/dialogs/ChangeThemeDialog.kt
  39. 4 4
      app/src/main/java/com/simplauncher/ui/dialogs/ChooseAlignmentDialog.kt
  40. 4 4
      app/src/main/java/com/simplauncher/ui/dialogs/ChooseClockTypeDialog.kt
  41. 3 3
      app/src/main/java/com/simplauncher/ui/dialogs/ChooseQuickButtonDialog.kt
  42. 4 4
      app/src/main/java/com/simplauncher/ui/dialogs/ChooseSearchBarPositionDialog.kt
  43. 2 2
      app/src/main/java/com/simplauncher/ui/dialogs/ChooseTimeFormatDialog.kt
  44. 35 0
      app/src/main/java/com/simplauncher/ui/dialogs/CreateFolderDialog.kt
  45. 45 0
      app/src/main/java/com/simplauncher/ui/dialogs/ManageAppFoldersDialog.kt
  46. 4 4
      app/src/main/java/com/simplauncher/ui/dialogs/RemoveAllAppsDialog.kt
  47. 5 6
      app/src/main/java/com/simplauncher/ui/dialogs/RenameAppDialog.kt
  48. 5 6
      app/src/main/java/com/simplauncher/ui/dialogs/RenameAppDisplayNameDialog.kt
  49. 46 0
      app/src/main/java/com/simplauncher/ui/dialogs/RenameFolderDialog.kt
  50. 2 2
      app/src/main/java/com/simplauncher/ui/main/AnalogClockView.kt
  51. 2 2
      app/src/main/java/com/simplauncher/ui/main/BinaryClockView.kt
  52. 1 1
      app/src/main/java/com/simplauncher/ui/main/ClockView.kt
  53. 105 66
      app/src/main/java/com/simplauncher/ui/main/HomeFragment.kt
  54. 28 19
      app/src/main/java/com/simplauncher/ui/options/AddAppFragment.kt
  55. 44 30
      app/src/main/java/com/simplauncher/ui/options/CustomiseAppsFragment.kt
  56. 54 0
      app/src/main/java/com/simplauncher/ui/options/CustomizeAppDrawerAppListFragment.kt
  57. 30 20
      app/src/main/java/com/simplauncher/ui/options/CustomizeAppDrawerFragment.kt
  58. 96 0
      app/src/main/java/com/simplauncher/ui/options/CustomizeFolderDetailFragment.kt
  59. 63 0
      app/src/main/java/com/simplauncher/ui/options/CustomizeFoldersFragment.kt
  60. 26 20
      app/src/main/java/com/simplauncher/ui/options/CustomizeQuickButtonsFragment.kt
  61. 38 38
      app/src/main/java/com/simplauncher/ui/options/CustomizeSearchFieldFragment.kt
  62. 39 41
      app/src/main/java/com/simplauncher/ui/options/OptionsFragment.kt
  63. 3 3
      app/src/main/java/com/simplauncher/utils/BaseFragment.kt
  64. 1 1
      app/src/main/java/com/simplauncher/utils/HomeWatcher.kt
  65. 1 1
      app/src/main/java/com/simplauncher/utils/IPublisher.kt
  66. 3 0
      app/src/main/java/com/simplauncher/utils/ISubscriber.kt
  67. 7 0
      app/src/main/java/com/simplauncher/utils/OnAppClickedListener.kt
  68. 1 1
      app/src/main/java/com/simplauncher/utils/OnItemActionListener.kt
  69. 2 2
      app/src/main/java/com/simplauncher/utils/OnLaunchAppListener.kt
  70. 2 2
      app/src/main/java/com/simplauncher/utils/OnShitDoneToAppsListener.kt
  71. 2 2
      app/src/main/java/com/simplauncher/utils/SystemUiManager.kt
  72. 3 3
      app/src/main/java/com/simplauncher/utils/Utils.kt
  73. 3 3
      app/src/main/java/com/simplauncher/utils/WallpaperManager.kt
  74. 20 0
      app/src/main/proto/app_drawer_folders.proto
  75. 1 1
      app/src/main/proto/core_preferences.proto
  76. 1 1
      app/src/main/proto/quick_button_preferences.proto
  77. 1 1
      app/src/main/proto/unlauncher_apps.proto
  78. 9 0
      app/src/main/res/drawable/ic_folder.xml
  79. 9 0
      app/src/main/res/drawable/ic_folder_arrow.xml
  80. 35 0
      app/src/main/res/layout/app_drawer_folder_item.xml
  81. 11 0
      app/src/main/res/layout/customize_app_drawer_fragment.xml
  82. 8 0
      app/src/main/res/layout/customize_folder_detail_app_item.xml
  83. 59 0
      app/src/main/res/layout/customize_folder_detail_fragment.xml
  84. 61 0
      app/src/main/res/layout/customize_folders_fragment.xml
  85. 24 0
      app/src/main/res/layout/customize_folders_list_item.xml
  86. 2 2
      app/src/main/res/layout/home_fragment_content.xml
  87. 5 1
      app/src/main/res/layout/imageview_border.xml
  88. 4 0
      app/src/main/res/menu/app_long_press_menu.xml
  89. 12 0
      app/src/main/res/menu/folder_detail_menu.xml
  90. 29 8
      app/src/main/res/navigation/nav_graph.xml
  91. 1 1
      app/src/main/res/values-de/strings.xml
  92. 8 1
      app/src/main/res/values/strings.xml

+ 5 - 3
app/build.gradle.kts

@@ -5,14 +5,13 @@ plugins {
     id("dagger.hilt.android.plugin")
     id("com.google.protobuf") version "0.9.0"
     kotlin("android")
-    kotlin("android.extensions")
     kotlin("kapt")
 }
 
 android {
     compileSdkVersion(33)
     defaultConfig {
-        applicationId = "com.jkuester.unlauncher"
+        applicationId = "com.simplauncher"
         minSdkVersion(21)
         targetSdkVersion(33)
         versionName = "2.1.1"
@@ -58,7 +57,10 @@ android {
     testOptions {
         unitTests.isIncludeAndroidResources = true
     }
-    namespace = "com.sduduzog.slimlauncher"
+    buildFeatures {
+        viewBinding = true
+    }
+    namespace = "com.simplauncher"
     applicationVariants.all{
         outputs.all {
             (this as com.android.build.gradle.internal.api.BaseVariantOutputImpl).outputFileName = "${applicationId}.apk"

+ 0 - 183
app/src/main/java/com/sduduzog/slimlauncher/adapters/AppDrawerAdapter.kt

@@ -1,183 +0,0 @@
-package com.sduduzog.slimlauncher.adapters
-
-import android.annotation.SuppressLint
-import android.text.Editable
-import android.text.TextWatcher
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.lifecycle.LifecycleOwner
-import androidx.recyclerview.widget.RecyclerView
-import com.jkuester.unlauncher.datastore.UnlauncherApp
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.ui.main.HomeFragment
-import com.sduduzog.slimlauncher.utils.firstUppercase
-import com.sduduzog.slimlauncher.utils.gravity
-
-class AppDrawerAdapter(
-        private val listener: HomeFragment.AppDrawerListener,
-        lifecycleOwner: LifecycleOwner,
-        private val unlauncherDataSource: UnlauncherDataSource
-) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
-
-    private val WORK_APP_PREFIX = "\uD83C\uDD46 " //Unicode for boxed w
-    private val regex = Regex("[!@#\$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/? ]")
-    private var apps: List<UnlauncherApp> = listOf()
-    private var filteredApps: List<AppDrawerRow> = listOf()
-    private var gravity = 3
-
-    init {
-        unlauncherDataSource.unlauncherAppsRepo.liveData().observe(lifecycleOwner) { unlauncherApps ->
-            apps = unlauncherApps.appsList
-            updateFilteredApps()
-        }
-        unlauncherDataSource.corePreferencesRepo.liveData().observe(lifecycleOwner) { corePrefs ->
-            gravity = corePrefs.alignmentFormat.gravity()
-            updateFilteredApps()
-        }
-    }
-
-    override fun getItemCount(): Int = filteredApps.size
-
-    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
-        when (val drawerRow = filteredApps[position]) {
-            is AppDrawerRow.Item -> {
-                val unlauncherApp = drawerRow.app
-                (holder as ItemViewHolder).bind(unlauncherApp)
-                holder.itemView.setOnClickListener {
-                    listener.onAppClicked(unlauncherApp)
-                }
-                holder.itemView.setOnLongClickListener {
-                    listener.onAppLongClicked(unlauncherApp, it)
-                }
-            }
-
-            is AppDrawerRow.Header -> (holder as HeaderViewHolder).bind(drawerRow.letter)
-        }
-    }
-
-    fun getFirstApp(): UnlauncherApp {
-        return filteredApps.filterIsInstance<AppDrawerRow.Item>().first().app
-    }
-
-    override fun getItemViewType(position: Int): Int = filteredApps[position].rowType.ordinal
-
-    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
-        val inflater = LayoutInflater.from(parent.context)
-        return when (RowType.values()[viewType]) {
-            RowType.App -> ItemViewHolder(
-                inflater.inflate(R.layout.add_app_fragment_list_item, parent, false)
-            )
-
-            RowType.Header -> HeaderViewHolder(
-                inflater.inflate(R.layout.app_drawer_fragment_header_item, parent, false)
-            )
-        }
-    }
-
-
-    private fun onlyFirstStringStartsWith(first: String, second: String, query: String) : Boolean {
-        return first.startsWith(query, true) and !second.startsWith(query, true);
-    }
-
-    fun setAppFilter(query: String = "") {
-        val filterQuery = regex.replace(query, "")
-        updateFilteredApps(filterQuery)
-    }
-
-    @SuppressLint("NotifyDataSetChanged")
-    private fun updateFilteredApps(filterQuery: String = "") {
-        val corePreferences = unlauncherDataSource.corePreferencesRepo.get()
-        val showDrawerHeadings = corePreferences.showDrawerHeadings
-        val searchAllApps = corePreferences.searchAllAppsInDrawer && filterQuery != ""
-        val displayableApps = apps
-            .filter { app ->
-                (app.displayInDrawer || searchAllApps) && regex.replace(app.displayName, "")
-                        .contains(filterQuery, ignoreCase = true)
-            }
-
-        val includeHeadings = !showDrawerHeadings || filterQuery != ""
-        val updatedApps = when (includeHeadings) {
-            true -> displayableApps
-                .sortedWith { a, b ->
-                    when {
-                        // if an app's name starts with the query prefer it
-                        onlyFirstStringStartsWith(a.displayName, b.displayName, filterQuery) -> -1
-                        onlyFirstStringStartsWith(b.displayName, a.displayName, filterQuery) -> 1
-                        // if both or none start with the query sort in normal oder
-                        else -> a.displayName.compareTo(b.displayName, true)
-                    }
-                }.map { AppDrawerRow.Item(it) }
-            // building a list with each letter and filtered app resulting in a list of
-            // [
-            // Header<"G">, App<"Gmail">, App<"Google Drive">, Header<"Y">, App<"YouTube">, ...
-            // ]
-            false -> displayableApps
-                .groupBy { app ->
-                    if(app.displayName.startsWith(WORK_APP_PREFIX)) WORK_APP_PREFIX
-                    else app.displayName.firstUppercase()
-                }.flatMap { entry ->
-                    listOf(
-                            AppDrawerRow.Header(entry.key),
-                            *(entry.value.map { AppDrawerRow.Item(it) }).toTypedArray()
-                    )
-                }
-        }
-        if (updatedApps != filteredApps) {
-            filteredApps = updatedApps
-            notifyDataSetChanged()
-        }
-    }
-
-    val searchBoxListener: TextWatcher = object : TextWatcher {
-        override fun afterTextChanged(s: Editable?) {
-            // Do nothing
-        }
-
-        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
-            // Do nothing
-        }
-
-        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
-            setAppFilter(s.toString())
-        }
-    }
-
-    inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
-
-        val item: TextView = itemView.findViewById(R.id.aa_list_item_app_name)
-
-        override fun toString(): String {
-            return "${super.toString()} '${item.text}'"
-        }
-
-        fun bind(item: UnlauncherApp) {
-            this.item.text = item.displayName
-            this.item.gravity = gravity
-        }
-    }
-
-    inner class HeaderViewHolder(headerView: View) : RecyclerView.ViewHolder(headerView) {
-        private val header: TextView = itemView.findViewById(R.id.aa_list_header_letter)
-
-        override fun toString(): String {
-            return "${super.toString()} '${header.text}'"
-        }
-
-        fun bind(letter: String) {
-            header.text = letter
-        }
-    }
-}
-
-enum class RowType {
-    Header, App
-}
-
-sealed class AppDrawerRow(val rowType: RowType) {
-    data class Item(val app: UnlauncherApp) : AppDrawerRow(RowType.App)
-
-    data class Header(val letter: String) : AppDrawerRow(RowType.Header)
-}

+ 0 - 49
app/src/main/java/com/sduduzog/slimlauncher/ui/options/CustomizeAppDrawerAppListFragment.kt

@@ -1,49 +0,0 @@
-package com.sduduzog.slimlauncher.ui.options
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.adapters.CustomizeAppDrawerAppsAdapter
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.utils.BaseFragment
-import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.customize_app_drawer_app_list_fragment.customize_app_drawer_fragment
-import kotlinx.android.synthetic.main.customize_app_drawer_app_list_fragment.customize_app_drawer_fragment_app_list
-import kotlinx.android.synthetic.main.customize_app_drawer_app_list_fragment.customize_app_drawer_fragment_app_progress_bar
-import kotlinx.android.synthetic.main.customize_app_drawer_app_list_fragment.customize_app_drawer_fragment_back
-import javax.inject.Inject
-
-@AndroidEntryPoint
-class CustomizeAppDrawerAppListFragment : BaseFragment() {
-    @Inject
-    lateinit var unlauncherDataSource: UnlauncherDataSource
-
-    override fun getFragmentView(): ViewGroup = customize_app_drawer_fragment
-
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View? {
-        return inflater.inflate(R.layout.customize_app_drawer_app_list_fragment, container, false)
-    }
-
-    override fun onActivityCreated(savedInstanceState: Bundle?) {
-        super.onActivityCreated(savedInstanceState)
-        val unlauncherAppsRepo = unlauncherDataSource.unlauncherAppsRepo
-        customize_app_drawer_fragment_app_list.adapter =
-            CustomizeAppDrawerAppsAdapter(viewLifecycleOwner, unlauncherAppsRepo)
-        unlauncherAppsRepo.liveData().observe(viewLifecycleOwner) {
-            it?.let {
-                customize_app_drawer_fragment_app_progress_bar.visibility = View.GONE
-            } ?: run {
-                customize_app_drawer_fragment_app_progress_bar.visibility = View.VISIBLE
-            }
-        }
-        customize_app_drawer_fragment_back.setOnClickListener {
-            requireActivity().onBackPressedDispatcher.onBackPressed()
-        }
-    }
-}

+ 0 - 3
app/src/main/java/com/sduduzog/slimlauncher/utils/ISubscriber.kt

@@ -1,3 +0,0 @@
-package com.sduduzog.slimlauncher.utils
-
-interface ISubscriber

+ 0 - 7
app/src/main/java/com/sduduzog/slimlauncher/utils/OnAppClickedListener.kt

@@ -1,7 +0,0 @@
-package com.sduduzog.slimlauncher.utils
-
-import com.sduduzog.slimlauncher.data.model.App
-
-interface OnAppClickedListener{
-    fun onAppClicked(app: App)
-}

+ 1 - 1
app/src/main/java/com/sduduzog/slimlauncher/App.kt → app/src/main/java/com/simplauncher/App.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher
+package com.simplauncher
 
 import android.app.Application
 import dagger.hilt.android.HiltAndroidApp

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/MainActivity.kt → app/src/main/java/com/simplauncher/MainActivity.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher
+package com.simplauncher
 
 import android.annotation.SuppressLint
 import android.content.SharedPreferences
@@ -16,7 +16,7 @@ import androidx.constraintlayout.motion.widget.MotionLayout
 import androidx.navigation.NavController
 import androidx.navigation.Navigation.findNavController
 import androidx.recyclerview.widget.RecyclerView
-import com.sduduzog.slimlauncher.utils.*
+import com.simplauncher.utils.*
 import dagger.hilt.android.AndroidEntryPoint
 import java.lang.reflect.Method
 import javax.inject.Inject

+ 4 - 4
app/src/main/java/com/sduduzog/slimlauncher/adapters/AddAppAdapter.kt → app/src/main/java/com/simplauncher/adapters/AddAppAdapter.kt

@@ -1,13 +1,13 @@
-package com.sduduzog.slimlauncher.adapters
+package com.simplauncher.adapters
 
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.data.model.App
-import com.sduduzog.slimlauncher.utils.OnAppClickedListener
+import com.simplauncher.R
+import com.simplauncher.data.model.App
+import com.simplauncher.utils.OnAppClickedListener
 
 class AddAppAdapter(private val listener: OnAppClickedListener) : RecyclerView.Adapter<AddAppAdapter.ViewHolder>() {
 

+ 280 - 0
app/src/main/java/com/simplauncher/adapters/AppDrawerAdapter.kt

@@ -0,0 +1,280 @@
+package com.simplauncher.adapters
+
+import android.annotation.SuppressLint
+import android.text.Editable
+import android.text.TextWatcher
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.lifecycle.LifecycleOwner
+import androidx.recyclerview.widget.RecyclerView
+import com.simplauncher.datastore.AppDrawerFolder
+import com.simplauncher.datastore.AppDrawerFolders
+import com.simplauncher.datastore.UnlauncherApp
+import com.simplauncher.R
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.ui.main.HomeFragment
+import com.simplauncher.utils.firstUppercase
+import com.simplauncher.utils.gravity
+
+class AppDrawerAdapter(
+        private val listener: HomeFragment.AppDrawerListener,
+        lifecycleOwner: LifecycleOwner,
+        private val unlauncherDataSource: UnlauncherDataSource
+) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+
+    private val WORK_APP_PREFIX = "\uD83C\uDD46 " //Unicode for boxed w
+    private val regex = Regex("[!@#\$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/? ]")
+    private var apps: List<UnlauncherApp> = listOf()
+    private var folders: AppDrawerFolders = AppDrawerFolders.getDefaultInstance()
+    private val expandedFolderIds: MutableSet<String> = mutableSetOf()
+    private var filteredApps: List<AppDrawerRow> = listOf()
+    private var gravity = 3
+
+    init {
+        unlauncherDataSource.unlauncherAppsRepo.liveData().observe(lifecycleOwner) { unlauncherApps ->
+            apps = unlauncherApps.appsList
+            updateFilteredApps()
+        }
+        unlauncherDataSource.corePreferencesRepo.liveData().observe(lifecycleOwner) { corePrefs ->
+            gravity = corePrefs.alignmentFormat.gravity()
+            updateFilteredApps()
+        }
+        unlauncherDataSource.appDrawerFoldersRepo.liveData().observe(lifecycleOwner) { appDrawerFolders ->
+            folders = appDrawerFolders
+            updateFilteredApps()
+        }
+    }
+
+    override fun getItemCount(): Int = filteredApps.size
+
+    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+        when (val drawerRow = filteredApps[position]) {
+            is AppDrawerRow.Item -> {
+                val unlauncherApp = drawerRow.app
+                (holder as ItemViewHolder).bind(unlauncherApp)
+                holder.itemView.setOnClickListener {
+                    listener.onAppClicked(unlauncherApp)
+                }
+                holder.itemView.setOnLongClickListener {
+                    listener.onAppLongClicked(unlauncherApp, it)
+                }
+            }
+
+            is AppDrawerRow.Header -> (holder as HeaderViewHolder).bind(drawerRow.letter)
+
+            is AppDrawerRow.FolderHeader -> {
+                (holder as FolderViewHolder).bind(drawerRow.folder, drawerRow.isExpanded)
+                holder.itemView.setOnClickListener { toggleFolder(drawerRow.folder.id) }
+            }
+
+            is AppDrawerRow.FolderItem -> {
+                val app = drawerRow.app
+                (holder as FolderAppViewHolder).bind(app, gravity, drawerRow.isLastInFolder)
+                holder.itemView.setOnClickListener { listener.onAppClicked(app) }
+                holder.itemView.setOnLongClickListener { listener.onAppLongClicked(app, it) }
+            }
+        }
+    }
+
+    fun getFirstApp(): UnlauncherApp {
+        return filteredApps.filterIsInstance<AppDrawerRow.Item>().first().app
+    }
+
+    override fun getItemViewType(position: Int): Int = filteredApps[position].rowType.ordinal
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        val inflater = LayoutInflater.from(parent.context)
+        return when (RowType.values()[viewType]) {
+            RowType.App -> ItemViewHolder(
+                inflater.inflate(R.layout.add_app_fragment_list_item, parent, false)
+            )
+            RowType.Header -> HeaderViewHolder(
+                inflater.inflate(R.layout.app_drawer_fragment_header_item, parent, false)
+            )
+            RowType.FolderHeader -> FolderViewHolder(
+                inflater.inflate(R.layout.app_drawer_folder_item, parent, false)
+            )
+            RowType.FolderApp -> FolderAppViewHolder(
+                inflater.inflate(R.layout.add_app_fragment_list_item, parent, false)
+            )
+        }
+    }
+
+    private fun toggleFolder(folderId: String) {
+        if (expandedFolderIds.contains(folderId)) {
+            expandedFolderIds.remove(folderId)
+        } else {
+            expandedFolderIds.add(folderId)
+        }
+        updateFilteredApps()
+    }
+
+    private fun buildFolderRows(): List<AppDrawerRow> {
+        val rows = mutableListOf<AppDrawerRow>()
+        for (folder in folders.foldersList) {
+            val isExpanded = expandedFolderIds.contains(folder.id)
+            rows.add(AppDrawerRow.FolderHeader(folder, isExpanded))
+            if (isExpanded) {
+                val resolvedApps = folder.appsList.mapNotNull { folderApp ->
+                    apps.firstOrNull {
+                        it.packageName == folderApp.packageName && it.className == folderApp.className
+                    }
+                }
+                resolvedApps.forEachIndexed { index, app ->
+                    rows.add(AppDrawerRow.FolderItem(app, isLastInFolder = index == resolvedApps.lastIndex))
+                }
+            }
+        }
+        return rows
+    }
+
+    private fun onlyFirstStringStartsWith(first: String, second: String, query: String) : Boolean {
+        return first.startsWith(query, true) and !second.startsWith(query, true);
+    }
+
+    fun setAppFilter(query: String = "") {
+        val filterQuery = regex.replace(query, "")
+        updateFilteredApps(filterQuery)
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    private fun updateFilteredApps(filterQuery: String = "") {
+        val corePreferences = unlauncherDataSource.corePreferencesRepo.get()
+        val showDrawerHeadings = corePreferences.showDrawerHeadings
+        val searchAllApps = corePreferences.searchAllAppsInDrawer && filterQuery != ""
+        val displayableApps = apps
+            .filter { app ->
+                (app.displayInDrawer || searchAllApps) && regex.replace(app.displayName, "")
+                        .contains(filterQuery, ignoreCase = true)
+            }
+
+        val includeHeadings = !showDrawerHeadings || filterQuery != ""
+        val appRows = when (includeHeadings) {
+            true -> displayableApps
+                .sortedWith { a, b ->
+                    when {
+                        onlyFirstStringStartsWith(a.displayName, b.displayName, filterQuery) -> -1
+                        onlyFirstStringStartsWith(b.displayName, a.displayName, filterQuery) -> 1
+                        else -> a.displayName.compareTo(b.displayName, true)
+                    }
+                }.map { AppDrawerRow.Item(it) }
+            false -> displayableApps
+                .groupBy { app ->
+                    if(app.displayName.startsWith(WORK_APP_PREFIX)) WORK_APP_PREFIX
+                    else app.displayName.firstUppercase()
+                }.flatMap { entry ->
+                    listOf(
+                            AppDrawerRow.Header(entry.key),
+                            *(entry.value.map { AppDrawerRow.Item(it) }).toTypedArray()
+                    )
+                }
+        }
+
+        val updatedApps = if (filterQuery.isEmpty() && folders.foldersCount > 0) {
+            buildFolderRows() + appRows
+        } else {
+            appRows
+        }
+
+        if (updatedApps != filteredApps) {
+            filteredApps = updatedApps
+            notifyDataSetChanged()
+        }
+    }
+
+    val searchBoxListener: TextWatcher = object : TextWatcher {
+        override fun afterTextChanged(s: Editable?) {}
+
+        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+
+        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+            setAppFilter(s.toString())
+        }
+    }
+
+    inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+        val item: TextView = itemView.findViewById(R.id.aa_list_item_app_name)
+
+        override fun toString(): String = "${super.toString()} '${item.text}'"
+
+        fun bind(item: UnlauncherApp) {
+            this.item.text = item.displayName
+            this.item.gravity = gravity
+        }
+    }
+
+    inner class HeaderViewHolder(headerView: View) : RecyclerView.ViewHolder(headerView) {
+        private val header: TextView = itemView.findViewById(R.id.aa_list_header_letter)
+
+        override fun toString(): String = "${super.toString()} '${header.text}'"
+
+        fun bind(letter: String) {
+            header.text = letter
+        }
+    }
+
+    inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+        private val name: TextView = itemView.findViewById(R.id.folder_item_name)
+        private val arrowLeft: ImageView = itemView.findViewById(R.id.folder_item_arrow_left)
+        private val arrowRight: ImageView = itemView.findViewById(R.id.folder_item_arrow_right)
+
+        fun bind(folder: AppDrawerFolder, isExpanded: Boolean) {
+            name.text = folder.name
+            name.gravity = gravity
+
+            val expandedRotation = 0f // points down
+            when (gravity) {
+                Gravity.LEFT, Gravity.START -> {
+                    arrowLeft.visibility = View.VISIBLE
+                    arrowRight.visibility = View.GONE
+                    arrowLeft.animate().rotation(if (isExpanded) expandedRotation else -90f).setDuration(200).start()
+                }
+                Gravity.RIGHT, Gravity.END -> {
+                    arrowLeft.visibility = View.GONE
+                    arrowRight.visibility = View.VISIBLE
+                    arrowRight.animate().rotation(if (isExpanded) expandedRotation else 90f).setDuration(200).start()
+                }
+                else -> { // CENTER_HORIZONTAL (1)
+                    arrowLeft.visibility = View.VISIBLE
+                    arrowRight.visibility = View.VISIBLE
+                    arrowLeft.animate().rotation(if (isExpanded) expandedRotation else -90f).setDuration(200).start()
+                    arrowRight.animate().rotation(if (isExpanded) expandedRotation else 90f).setDuration(200).start()
+                }
+            }
+        }
+    }
+
+    inner class FolderAppViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+        private val item: TextView = itemView.findViewById(R.id.aa_list_item_app_name)
+
+        fun bind(app: UnlauncherApp, gravity: Int, isLastInFolder: Boolean) {
+            item.text = app.displayName
+            item.gravity = gravity
+            val indentPx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48f, item.resources.displayMetrics).toInt()
+            val bottomPadPx = if (isLastInFolder) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24f, item.resources.displayMetrics).toInt() else 0
+            val basePad = item.paddingTop
+            when (gravity) {
+                Gravity.LEFT, Gravity.START -> item.setPadding(indentPx, basePad, item.paddingRight, bottomPadPx)
+                Gravity.RIGHT, Gravity.END -> item.setPadding(item.paddingLeft, basePad, indentPx, bottomPadPx)
+                else -> item.setPadding(item.paddingLeft, basePad, item.paddingRight, bottomPadPx)
+            }
+        }
+    }
+}
+
+enum class RowType {
+    Header, App, FolderHeader, FolderApp
+}
+
+sealed class AppDrawerRow(val rowType: RowType) {
+    data class Item(val app: UnlauncherApp) : AppDrawerRow(RowType.App)
+    data class Header(val letter: String) : AppDrawerRow(RowType.Header)
+    data class FolderHeader(val folder: AppDrawerFolder, val isExpanded: Boolean) : AppDrawerRow(RowType.FolderHeader)
+    data class FolderItem(val app: UnlauncherApp, val isLastInFolder: Boolean = false) : AppDrawerRow(RowType.FolderApp)
+}

+ 5 - 5
app/src/main/java/com/sduduzog/slimlauncher/adapters/CustomAppsAdapter.kt → app/src/main/java/com/simplauncher/adapters/CustomAppsAdapter.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.adapters
+package com.simplauncher.adapters
 
 import android.annotation.SuppressLint
 import android.view.LayoutInflater
@@ -9,10 +9,10 @@ import android.widget.ImageView
 import android.widget.TextView
 import androidx.recyclerview.widget.ItemTouchHelper
 import androidx.recyclerview.widget.RecyclerView
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.models.HomeApp
-import com.sduduzog.slimlauncher.utils.OnItemActionListener
-import com.sduduzog.slimlauncher.utils.OnShitDoneToAppsListener
+import com.simplauncher.R
+import com.simplauncher.models.HomeApp
+import com.simplauncher.utils.OnItemActionListener
+import com.simplauncher.utils.OnShitDoneToAppsListener
 
 class CustomAppsAdapter(private val listener: OnShitDoneToAppsListener) : RecyclerView.Adapter<CustomAppsAdapter.ViewHolder>(), OnItemActionListener {
 

+ 4 - 4
app/src/main/java/com/sduduzog/slimlauncher/adapters/CustomizeAppDrawerAppsAdapter.kt → app/src/main/java/com/simplauncher/adapters/CustomizeAppDrawerAppsAdapter.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.adapters
+package com.simplauncher.adapters
 
 import android.view.LayoutInflater
 import android.view.View
@@ -6,9 +6,9 @@ import android.view.ViewGroup
 import android.widget.CheckBox
 import androidx.lifecycle.LifecycleOwner
 import androidx.recyclerview.widget.RecyclerView
-import com.jkuester.unlauncher.datastore.UnlauncherApps
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsRepository
+import com.simplauncher.datastore.UnlauncherApps
+import com.simplauncher.R
+import com.simplauncher.datasource.apps.UnlauncherAppsRepository
 
 class CustomizeAppDrawerAppsAdapter(
     lifecycleOwner: LifecycleOwner, private val appsRepo: UnlauncherAppsRepository

+ 48 - 0
app/src/main/java/com/simplauncher/adapters/CustomizeFolderAppsAdapter.kt

@@ -0,0 +1,48 @@
+package com.simplauncher.adapters
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.CheckBox
+import androidx.recyclerview.widget.RecyclerView
+import com.simplauncher.R
+import com.simplauncher.datastore.UnlauncherApp
+
+class CustomizeFolderAppsAdapter(
+    private val onAppToggled: (UnlauncherApp, Boolean) -> Unit
+) : RecyclerView.Adapter<CustomizeFolderAppsAdapter.ViewHolder>() {
+
+    private var items: List<Pair<UnlauncherApp, Boolean>> = emptyList()
+
+    @SuppressLint("NotifyDataSetChanged")
+    fun setItems(allApps: List<UnlauncherApp>, folderPackageKeys: Set<String>) {
+        items = allApps
+            .sortedBy { it.displayName.lowercase() }
+            .map { app -> app to (appKey(app) in folderPackageKeys) }
+        notifyDataSetChanged()
+    }
+
+    override fun getItemCount() = items.size
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.customize_folder_detail_app_item, parent, false)
+        return ViewHolder(view as CheckBox)
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val (app, isInFolder) = items[position]
+        holder.checkbox.text = app.displayName
+        holder.checkbox.setOnCheckedChangeListener(null)
+        holder.checkbox.isChecked = isInFolder
+        holder.checkbox.setOnCheckedChangeListener { _, checked ->
+            onAppToggled(app, checked)
+        }
+    }
+
+    class ViewHolder(val checkbox: CheckBox) : RecyclerView.ViewHolder(checkbox)
+
+    companion object {
+        fun appKey(app: UnlauncherApp) = "${app.packageName}/${app.className}"
+    }
+}

+ 41 - 0
app/src/main/java/com/simplauncher/adapters/CustomizeFoldersAdapter.kt

@@ -0,0 +1,41 @@
+package com.simplauncher.adapters
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.simplauncher.R
+import com.simplauncher.datastore.AppDrawerFolder
+
+class CustomizeFoldersAdapter(
+    private val onFolderClicked: (AppDrawerFolder) -> Unit
+) : RecyclerView.Adapter<CustomizeFoldersAdapter.ViewHolder>() {
+
+    private var folders: List<AppDrawerFolder> = emptyList()
+
+    @SuppressLint("NotifyDataSetChanged")
+    fun setFolders(folders: List<AppDrawerFolder>) {
+        this.folders = folders
+        notifyDataSetChanged()
+    }
+
+    override fun getItemCount() = folders.size
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.customize_folders_list_item, parent, false)
+        return ViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val folder = folders[position]
+        holder.name.text = folder.name
+        holder.itemView.setOnClickListener { onFolderClicked(folder) }
+    }
+
+    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+        val name: TextView = view.findViewById(R.id.folder_name)
+    }
+}

+ 6 - 6
app/src/main/java/com/sduduzog/slimlauncher/adapters/HomeAdapter.kt → app/src/main/java/com/simplauncher/adapters/HomeAdapter.kt

@@ -1,15 +1,15 @@
-package com.sduduzog.slimlauncher.adapters
+package com.simplauncher.adapters
 
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.models.HomeApp
-import com.sduduzog.slimlauncher.ui.main.HomeFragment
-import com.sduduzog.slimlauncher.utils.gravity
+import com.simplauncher.R
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.models.HomeApp
+import com.simplauncher.ui.main.HomeFragment
+import com.simplauncher.utils.gravity
 
 class HomeAdapter(
     private val listener: HomeFragment,

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/data/BaseDao.kt → app/src/main/java/com/simplauncher/data/BaseDao.kt

@@ -1,8 +1,8 @@
-package com.sduduzog.slimlauncher.data
+package com.simplauncher.data
 
 import androidx.lifecycle.LiveData
 import androidx.room.*
-import com.sduduzog.slimlauncher.models.HomeApp
+import com.simplauncher.models.HomeApp
 
 @Dao
 interface BaseDao {

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/data/BaseDatabase.kt → app/src/main/java/com/simplauncher/data/BaseDatabase.kt

@@ -1,11 +1,11 @@
-package com.sduduzog.slimlauncher.data
+package com.simplauncher.data
 
 import android.os.Process
 import androidx.room.Database
 import androidx.room.RoomDatabase
 import androidx.room.migration.Migration
 import androidx.sqlite.db.SupportSQLiteDatabase
-import com.sduduzog.slimlauncher.models.HomeApp
+import com.simplauncher.models.HomeApp
 
 
 @Database(entities = [HomeApp::class], version = 9, exportSchema = false)

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/data/model/App.kt → app/src/main/java/com/simplauncher/data/model/App.kt

@@ -1,6 +1,6 @@
-package com.sduduzog.slimlauncher.data.model
+package com.simplauncher.data.model
 
-import com.sduduzog.slimlauncher.models.HomeApp
+import com.simplauncher.models.HomeApp
 
 data class App(
         val appName: String,

+ 23 - 13
app/src/main/java/com/sduduzog/slimlauncher/datasource/UnlauncherDataSource.kt → app/src/main/java/com/simplauncher/datasource/UnlauncherDataSource.kt

@@ -1,21 +1,29 @@
-package com.sduduzog.slimlauncher.datasource
+package com.simplauncher.datasource
 
 import android.content.Context
 import androidx.datastore.core.DataStore
 import androidx.datastore.dataStore
 import androidx.lifecycle.LifecycleCoroutineScope
-import com.jkuester.unlauncher.datastore.CorePreferences
-import com.jkuester.unlauncher.datastore.QuickButtonPreferences
-import com.jkuester.unlauncher.datastore.UnlauncherApps
-import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsMigrations
-import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsRepository
-import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsSerializer
-import com.sduduzog.slimlauncher.datasource.coreprefs.CorePreferencesMigrations
-import com.sduduzog.slimlauncher.datasource.coreprefs.CorePreferencesRepository
-import com.sduduzog.slimlauncher.datasource.coreprefs.CorePreferencesSerializer
-import com.sduduzog.slimlauncher.datasource.quickbuttonprefs.QuickButtonPreferencesMigrations
-import com.sduduzog.slimlauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
-import com.sduduzog.slimlauncher.datasource.quickbuttonprefs.QuickButtonPreferencesSerializer
+import com.simplauncher.datastore.AppDrawerFolders
+import com.simplauncher.datastore.CorePreferences
+import com.simplauncher.datastore.QuickButtonPreferences
+import com.simplauncher.datastore.UnlauncherApps
+import com.simplauncher.datasource.apps.UnlauncherAppsMigrations
+import com.simplauncher.datasource.apps.UnlauncherAppsRepository
+import com.simplauncher.datasource.apps.UnlauncherAppsSerializer
+import com.simplauncher.datasource.coreprefs.CorePreferencesMigrations
+import com.simplauncher.datasource.coreprefs.CorePreferencesRepository
+import com.simplauncher.datasource.coreprefs.CorePreferencesSerializer
+import com.simplauncher.datasource.folders.AppDrawerFoldersRepository
+import com.simplauncher.datasource.folders.AppDrawerFoldersSerializer
+import com.simplauncher.datasource.quickbuttonprefs.QuickButtonPreferencesMigrations
+import com.simplauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
+import com.simplauncher.datasource.quickbuttonprefs.QuickButtonPreferencesSerializer
+
+private val Context.appDrawerFoldersStore: DataStore<AppDrawerFolders> by dataStore(
+    fileName = "app_drawer_folders.proto",
+    serializer = AppDrawerFoldersSerializer
+)
 
 private val Context.quickButtonPreferencesStore: DataStore<QuickButtonPreferences> by dataStore(
     fileName = "quick_button_preferences.proto",
@@ -36,6 +44,8 @@ private val Context.corePreferencesStore: DataStore<CorePreferences> by dataStor
 )
 
 class UnlauncherDataSource(context: Context, lifecycleScope: LifecycleCoroutineScope) {
+    val appDrawerFoldersRepo =
+        AppDrawerFoldersRepository(context.appDrawerFoldersStore, lifecycleScope)
     val quickButtonPreferencesRepo =
         QuickButtonPreferencesRepository(context.quickButtonPreferencesStore, lifecycleScope)
     val unlauncherAppsRepo = UnlauncherAppsRepository(context.unlauncherAppsStore, lifecycleScope)

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/datasource/apps/UnlauncherAppsMigrations.kt → app/src/main/java/com/simplauncher/datasource/apps/UnlauncherAppsMigrations.kt

@@ -1,8 +1,8 @@
-package com.sduduzog.slimlauncher.datasource.apps
+package com.simplauncher.datasource.apps
 
 import android.content.Context
 import androidx.datastore.core.DataMigration
-import com.jkuester.unlauncher.datastore.UnlauncherApps
+import com.simplauncher.datastore.UnlauncherApps
 
 class UnlauncherAppsMigrations {
 

+ 5 - 5
app/src/main/java/com/sduduzog/slimlauncher/datasource/apps/UnlauncherAppsRepository.kt → app/src/main/java/com/simplauncher/datasource/apps/UnlauncherAppsRepository.kt

@@ -1,14 +1,14 @@
-package com.sduduzog.slimlauncher.datasource.apps
+package com.simplauncher.datasource.apps
 
 import android.util.Log
 import androidx.datastore.core.DataStore
 import androidx.lifecycle.LifecycleCoroutineScope
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.asLiveData
-import com.jkuester.unlauncher.datastore.UnlauncherApp
-import com.jkuester.unlauncher.datastore.UnlauncherApps
-import com.sduduzog.slimlauncher.data.model.App
-import com.sduduzog.slimlauncher.models.HomeApp
+import com.simplauncher.datastore.UnlauncherApp
+import com.simplauncher.datastore.UnlauncherApps
+import com.simplauncher.data.model.App
+import com.simplauncher.models.HomeApp
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.launch

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/datasource/apps/UnlauncherAppsSerializer.kt → app/src/main/java/com/simplauncher/datasource/apps/UnlauncherAppsSerializer.kt

@@ -1,9 +1,9 @@
-package com.sduduzog.slimlauncher.datasource.apps
+package com.simplauncher.datasource.apps
 
 import androidx.datastore.core.CorruptionException
 import androidx.datastore.core.Serializer
 import com.google.protobuf.InvalidProtocolBufferException
-import com.jkuester.unlauncher.datastore.UnlauncherApps
+import com.simplauncher.datastore.UnlauncherApps
 import java.io.InputStream
 import java.io.OutputStream
 

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/datasource/coreprefs/CorePreferencesMigrations.kt → app/src/main/java/com/simplauncher/datasource/coreprefs/CorePreferencesMigrations.kt

@@ -1,8 +1,8 @@
-package com.sduduzog.slimlauncher.datasource.coreprefs
+package com.simplauncher.datasource.coreprefs
 
 import androidx.datastore.core.DataMigration
-import com.jkuester.unlauncher.datastore.ClockType
-import com.jkuester.unlauncher.datastore.CorePreferences
+import com.simplauncher.datastore.ClockType
+import com.simplauncher.datastore.CorePreferences
 
 class CorePreferencesMigrations {
     fun get(): List<DataMigration<CorePreferences>> {

+ 5 - 5
app/src/main/java/com/sduduzog/slimlauncher/datasource/coreprefs/CorePreferencesRepository.kt → app/src/main/java/com/simplauncher/datasource/coreprefs/CorePreferencesRepository.kt

@@ -1,14 +1,14 @@
-package com.sduduzog.slimlauncher.datasource.coreprefs
+package com.simplauncher.datasource.coreprefs
 
 import android.util.Log
 import androidx.datastore.core.DataStore
 import androidx.lifecycle.LifecycleCoroutineScope
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.asLiveData
-import com.jkuester.unlauncher.datastore.AlignmentFormat
-import com.jkuester.unlauncher.datastore.ClockType
-import com.jkuester.unlauncher.datastore.CorePreferences
-import com.jkuester.unlauncher.datastore.SearchBarPosition
+import com.simplauncher.datastore.AlignmentFormat
+import com.simplauncher.datastore.ClockType
+import com.simplauncher.datastore.CorePreferences
+import com.simplauncher.datastore.SearchBarPosition
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.first

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/datasource/coreprefs/CorePreferencesSerializer.kt → app/src/main/java/com/simplauncher/datasource/coreprefs/CorePreferencesSerializer.kt

@@ -1,9 +1,9 @@
-package com.sduduzog.slimlauncher.datasource.coreprefs
+package com.simplauncher.datasource.coreprefs
 
 import androidx.datastore.core.CorruptionException
 import androidx.datastore.core.Serializer
 import com.google.protobuf.InvalidProtocolBufferException
-import com.jkuester.unlauncher.datastore.CorePreferences
+import com.simplauncher.datastore.CorePreferences
 import java.io.InputStream
 import java.io.OutputStream
 

+ 125 - 0
app/src/main/java/com/simplauncher/datasource/folders/AppDrawerFoldersRepository.kt

@@ -0,0 +1,125 @@
+package com.simplauncher.datasource.folders
+
+import android.util.Log
+import androidx.datastore.core.DataStore
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.asLiveData
+import com.simplauncher.datastore.AppDrawerFolder
+import com.simplauncher.datastore.AppDrawerFolders
+import com.simplauncher.datastore.FolderApp
+import com.simplauncher.datastore.UnlauncherApp
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import java.io.IOException
+import java.util.UUID
+
+class AppDrawerFoldersRepository(
+    private val appDrawerFoldersStore: DataStore<AppDrawerFolders>,
+    private val lifecycleScope: LifecycleCoroutineScope
+) {
+    private val appDrawerFoldersFlow: Flow<AppDrawerFolders> =
+        appDrawerFoldersStore.data
+            .catch { exception ->
+                if (exception is IOException) {
+                    Log.e("AppDrawerFoldersRepo", "Error reading folders.", exception)
+                    emit(AppDrawerFolders.getDefaultInstance())
+                } else {
+                    throw exception
+                }
+            }
+
+    fun liveData(): LiveData<AppDrawerFolders> = appDrawerFoldersFlow.asLiveData()
+
+    fun get(): AppDrawerFolders = runBlocking { appDrawerFoldersFlow.first() }
+
+    fun createFolder(name: String) {
+        lifecycleScope.launch {
+            appDrawerFoldersStore.updateData { current ->
+                val folder = AppDrawerFolder.newBuilder()
+                    .setId(UUID.randomUUID().toString())
+                    .setName(name)
+                    .build()
+                current.toBuilder().addFolders(folder).build()
+            }
+        }
+    }
+
+    fun renameFolder(folderId: String, name: String) {
+        lifecycleScope.launch {
+            appDrawerFoldersStore.updateData { current ->
+                val builder = current.toBuilder()
+                val index = builder.foldersList.indexOfFirst { it.id == folderId }
+                if (index >= 0) {
+                    builder.setFolders(index, builder.foldersList[index].toBuilder().setName(name))
+                }
+                builder.build()
+            }
+        }
+    }
+
+    fun deleteFolder(folderId: String) {
+        lifecycleScope.launch {
+            appDrawerFoldersStore.updateData { current ->
+                val builder = current.toBuilder()
+                val index = builder.foldersList.indexOfFirst { it.id == folderId }
+                if (index >= 0) builder.removeFolders(index)
+                builder.build()
+            }
+        }
+    }
+
+    fun addAppToFolder(folderId: String, app: UnlauncherApp) {
+        lifecycleScope.launch {
+            appDrawerFoldersStore.updateData { current ->
+                val builder = current.toBuilder()
+                val index = builder.foldersList.indexOfFirst { it.id == folderId }
+                if (index >= 0) {
+                    val folder = builder.foldersList[index]
+                    val alreadyIn = folder.appsList.any {
+                        it.packageName == app.packageName && it.className == app.className
+                    }
+                    if (!alreadyIn) {
+                        val folderApp = FolderApp.newBuilder()
+                            .setPackageName(app.packageName)
+                            .setClassName(app.className)
+                            .setUserSerial(app.userSerial)
+                            .build()
+                        builder.setFolders(index, folder.toBuilder().addApps(folderApp))
+                    }
+                }
+                builder.build()
+            }
+        }
+    }
+
+    fun removeAppFromFolder(folderId: String, app: UnlauncherApp) {
+        removeAppFromFolder(folderId, app.packageName, app.className)
+    }
+
+    fun removeAppFromFolder(folderId: String, folderApp: FolderApp) {
+        removeAppFromFolder(folderId, folderApp.packageName, folderApp.className)
+    }
+
+    private fun removeAppFromFolder(folderId: String, packageName: String, className: String) {
+        lifecycleScope.launch {
+            appDrawerFoldersStore.updateData { current ->
+                val builder = current.toBuilder()
+                val index = builder.foldersList.indexOfFirst { it.id == folderId }
+                if (index >= 0) {
+                    val folder = builder.foldersList[index]
+                    val appIndex = folder.appsList.indexOfFirst {
+                        it.packageName == packageName && it.className == className
+                    }
+                    if (appIndex >= 0) {
+                        builder.setFolders(index, folder.toBuilder().removeApps(appIndex))
+                    }
+                }
+                builder.build()
+            }
+        }
+    }
+}

+ 25 - 0
app/src/main/java/com/simplauncher/datasource/folders/AppDrawerFoldersSerializer.kt

@@ -0,0 +1,25 @@
+package com.simplauncher.datasource.folders
+
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.Serializer
+import com.google.protobuf.InvalidProtocolBufferException
+import com.simplauncher.datastore.AppDrawerFolders
+import java.io.InputStream
+import java.io.OutputStream
+
+object AppDrawerFoldersSerializer : Serializer<AppDrawerFolders> {
+    override val defaultValue: AppDrawerFolders = AppDrawerFolders.getDefaultInstance()
+
+    @Suppress("BlockingMethodInNonBlockingContext")
+    override suspend fun readFrom(input: InputStream): AppDrawerFolders {
+        try {
+            return AppDrawerFolders.parseFrom(input)
+        } catch (exception: InvalidProtocolBufferException) {
+            throw CorruptionException("Cannot read proto.", exception)
+        }
+    }
+
+    @Suppress("BlockingMethodInNonBlockingContext")
+    override suspend fun writeTo(t: AppDrawerFolders, output: OutputStream) =
+        t.writeTo(output)
+}

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/datasource/quickbuttonprefs/QuickButtonPreferencesMigrations.kt → app/src/main/java/com/simplauncher/datasource/quickbuttonprefs/QuickButtonPreferencesMigrations.kt

@@ -1,10 +1,10 @@
-package com.sduduzog.slimlauncher.datasource.quickbuttonprefs
+package com.simplauncher.datasource.quickbuttonprefs
 
 import android.content.Context
 import androidx.datastore.core.DataMigration
 import androidx.datastore.migrations.SharedPreferencesMigration
 import androidx.datastore.migrations.SharedPreferencesView
-import com.jkuester.unlauncher.datastore.QuickButtonPreferences
+import com.simplauncher.datastore.QuickButtonPreferences
 
 class QuickButtonPreferencesMigrations {
     fun get(context: Context): List<DataMigration<QuickButtonPreferences>> {

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/datasource/quickbuttonprefs/QuickButtonPreferencesRepository.kt → app/src/main/java/com/simplauncher/datasource/quickbuttonprefs/QuickButtonPreferencesRepository.kt

@@ -1,12 +1,12 @@
-package com.sduduzog.slimlauncher.datasource.quickbuttonprefs
+package com.simplauncher.datasource.quickbuttonprefs
 
 import android.util.Log
 import androidx.datastore.core.DataStore
 import androidx.lifecycle.LifecycleCoroutineScope
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.asLiveData
-import com.jkuester.unlauncher.datastore.QuickButtonPreferences
-import com.sduduzog.slimlauncher.R
+import com.simplauncher.datastore.QuickButtonPreferences
+import com.simplauncher.R
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.first

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/datasource/quickbuttonprefs/QuickButtonPreferencesSerializer.kt → app/src/main/java/com/simplauncher/datasource/quickbuttonprefs/QuickButtonPreferencesSerializer.kt

@@ -1,9 +1,9 @@
-package com.sduduzog.slimlauncher.datasource.quickbuttonprefs
+package com.simplauncher.datasource.quickbuttonprefs
 
 import androidx.datastore.core.CorruptionException
 import androidx.datastore.core.Serializer
 import com.google.protobuf.InvalidProtocolBufferException
-import com.jkuester.unlauncher.datastore.QuickButtonPreferences
+import com.simplauncher.datastore.QuickButtonPreferences
 import java.io.InputStream
 import java.io.OutputStream
 

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/di/ActivityModule.kt → app/src/main/java/com/simplauncher/di/ActivityModule.kt

@@ -1,10 +1,10 @@
-package com.sduduzog.slimlauncher.di
+package com.simplauncher.di
 
 import android.app.Activity
 import androidx.core.app.ComponentActivity
 import androidx.lifecycle.LifecycleCoroutineScope
 import androidx.lifecycle.lifecycleScope
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
+import com.simplauncher.datasource.UnlauncherDataSource
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/di/AppModule.kt → app/src/main/java/com/simplauncher/di/AppModule.kt

@@ -1,9 +1,9 @@
-package com.sduduzog.slimlauncher.di
+package com.simplauncher.di
 
 import android.app.Application
 import androidx.room.Room
-import com.sduduzog.slimlauncher.data.BaseDao
-import com.sduduzog.slimlauncher.data.BaseDatabase
+import com.simplauncher.data.BaseDao
+import com.simplauncher.data.BaseDatabase
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/di/RepositoryModule.kt → app/src/main/java/com/simplauncher/di/RepositoryModule.kt

@@ -1,7 +1,7 @@
-package com.sduduzog.slimlauncher.di
+package com.simplauncher.di
 
-import com.sduduzog.slimlauncher.data.BaseDao
-import com.sduduzog.slimlauncher.models.Repository
+import com.simplauncher.data.BaseDao
+import com.simplauncher.models.Repository
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/models/AddAppViewModel.kt → app/src/main/java/com/simplauncher/models/AddAppViewModel.kt

@@ -1,10 +1,10 @@
-package com.sduduzog.slimlauncher.models
+package com.simplauncher.models
 
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
-import com.sduduzog.slimlauncher.data.model.App
+import com.simplauncher.data.model.App
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch

+ 1 - 1
app/src/main/java/com/sduduzog/slimlauncher/models/CustomiseAppsViewModel.kt → app/src/main/java/com/simplauncher/models/CustomiseAppsViewModel.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.models
+package com.simplauncher.models
 
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.ViewModel

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/models/HomeApp.kt → app/src/main/java/com/simplauncher/models/HomeApp.kt

@@ -1,8 +1,8 @@
-package com.sduduzog.slimlauncher.models
+package com.simplauncher.models
 
 import androidx.room.ColumnInfo
 import androidx.room.Entity
-import com.sduduzog.slimlauncher.data.model.App
+import com.simplauncher.data.model.App
 
 @Entity(tableName = "home_apps", primaryKeys = ["package_name", "user_serial"])
 data class HomeApp(

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/models/MainViewModel.kt → app/src/main/java/com/simplauncher/models/MainViewModel.kt

@@ -1,8 +1,8 @@
-package com.sduduzog.slimlauncher.models
+package com.simplauncher.models
 
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.ViewModel
-import com.sduduzog.slimlauncher.data.model.App
+import com.simplauncher.data.model.App
 import dagger.hilt.android.lifecycle.HiltViewModel
 import javax.inject.Inject
 

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/models/Repository.kt → app/src/main/java/com/simplauncher/models/Repository.kt

@@ -1,7 +1,7 @@
-package com.sduduzog.slimlauncher.models
+package com.simplauncher.models
 
 import androidx.lifecycle.LiveData
-import com.sduduzog.slimlauncher.data.BaseDao
+import com.simplauncher.data.BaseDao
 
 class Repository(private val baseDao: BaseDao) {
 

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/ChangeThemeDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/ChangeThemeDialog.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.AlertDialog
 import android.app.Dialog
@@ -7,7 +7,7 @@ import android.content.SharedPreferences
 import android.os.Bundle
 import androidx.core.content.edit
 import androidx.fragment.app.DialogFragment
-import com.sduduzog.slimlauncher.R
+import com.simplauncher.R
 
 class ChangeThemeDialog : DialogFragment() {
 

+ 4 - 4
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/ChooseAlignmentDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/ChooseAlignmentDialog.kt

@@ -1,12 +1,12 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.AlertDialog
 import android.app.Dialog
 import android.os.Bundle
 import androidx.fragment.app.DialogFragment
-import com.jkuester.unlauncher.datastore.AlignmentFormat
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
+import com.simplauncher.datastore.AlignmentFormat
+import com.simplauncher.R
+import com.simplauncher.datasource.UnlauncherDataSource
 import dagger.hilt.android.AndroidEntryPoint
 import javax.inject.Inject
 

+ 4 - 4
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/ChooseClockTypeDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/ChooseClockTypeDialog.kt

@@ -1,12 +1,12 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.AlertDialog
 import android.app.Dialog
 import android.os.Bundle
 import androidx.fragment.app.DialogFragment
-import com.jkuester.unlauncher.datastore.ClockType
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
+import com.simplauncher.datastore.ClockType
+import com.simplauncher.R
+import com.simplauncher.datasource.UnlauncherDataSource
 import dagger.hilt.android.AndroidEntryPoint
 import javax.inject.Inject
 

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/ChooseQuickButtonDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/ChooseQuickButtonDialog.kt

@@ -1,12 +1,12 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.AlertDialog
 import android.app.Dialog
 import android.content.DialogInterface
 import android.os.Bundle
 import androidx.fragment.app.DialogFragment
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
+import com.simplauncher.R
+import com.simplauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
 
 class ChooseQuickButtonDialog(
     private val repo: QuickButtonPreferencesRepository,

+ 4 - 4
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/ChooseSearchBarPositionDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/ChooseSearchBarPositionDialog.kt

@@ -1,12 +1,12 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.AlertDialog
 import android.app.Dialog
 import android.os.Bundle
 import androidx.fragment.app.DialogFragment
-import com.jkuester.unlauncher.datastore.SearchBarPosition
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
+import com.simplauncher.datastore.SearchBarPosition
+import com.simplauncher.R
+import com.simplauncher.datasource.UnlauncherDataSource
 import dagger.hilt.android.AndroidEntryPoint
 import javax.inject.Inject
 

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/ChooseTimeFormatDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/ChooseTimeFormatDialog.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.AlertDialog
 import android.app.Dialog
@@ -7,7 +7,7 @@ import android.content.SharedPreferences
 import android.os.Bundle
 import androidx.core.content.edit
 import androidx.fragment.app.DialogFragment
-import com.sduduzog.slimlauncher.R
+import com.simplauncher.R
 
 class ChooseTimeFormatDialog : DialogFragment(){
 

+ 35 - 0
app/src/main/java/com/simplauncher/ui/dialogs/CreateFolderDialog.kt

@@ -0,0 +1,35 @@
+package com.simplauncher.ui.dialogs
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.widget.EditText
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import com.simplauncher.R
+import com.simplauncher.datasource.folders.AppDrawerFoldersRepository
+
+class CreateFolderDialog : DialogFragment() {
+
+    private lateinit var foldersRepo: AppDrawerFoldersRepository
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        val view = LayoutInflater.from(context).inflate(R.layout.rename_dialog_edit_text, null, false)
+        val editText: EditText = view.findViewById(R.id.rename_editText)
+        val builder = AlertDialog.Builder(requireContext())
+        builder.setTitle(R.string.create_folder)
+        builder.setView(view)
+        builder.setPositiveButton(android.R.string.ok) { _, _ ->
+            val name = editText.text.toString().trim()
+            if (name.isNotEmpty()) foldersRepo.createFolder(name)
+        }
+        builder.setNegativeButton(android.R.string.cancel, null)
+        return builder.create()
+    }
+
+    companion object {
+        fun getInstance(foldersRepo: AppDrawerFoldersRepository): CreateFolderDialog {
+            return CreateFolderDialog().apply { this.foldersRepo = foldersRepo }
+        }
+    }
+}

+ 45 - 0
app/src/main/java/com/simplauncher/ui/dialogs/ManageAppFoldersDialog.kt

@@ -0,0 +1,45 @@
+package com.simplauncher.ui.dialogs
+
+import android.app.Dialog
+import android.os.Bundle
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import com.simplauncher.R
+import com.simplauncher.datastore.UnlauncherApp
+import com.simplauncher.datasource.folders.AppDrawerFoldersRepository
+
+class ManageAppFoldersDialog : DialogFragment() {
+
+    private lateinit var app: UnlauncherApp
+    private lateinit var foldersRepo: AppDrawerFoldersRepository
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        val folders = foldersRepo.get().foldersList
+        val folderNames = folders.map { it.name }.toTypedArray()
+        val checkedItems = folders.map { folder ->
+            folder.appsList.any { it.packageName == app.packageName && it.className == app.className }
+        }.toBooleanArray()
+
+        val builder = AlertDialog.Builder(requireContext())
+        builder.setTitle(R.string.manage_app_folders)
+        builder.setMultiChoiceItems(folderNames, checkedItems) { _, which, isChecked ->
+            val folder = folders[which]
+            if (isChecked) {
+                foldersRepo.addAppToFolder(folder.id, app)
+            } else {
+                foldersRepo.removeAppFromFolder(folder.id, app)
+            }
+        }
+        builder.setPositiveButton(android.R.string.ok, null)
+        return builder.create()
+    }
+
+    companion object {
+        fun getInstance(app: UnlauncherApp, foldersRepo: AppDrawerFoldersRepository): ManageAppFoldersDialog {
+            return ManageAppFoldersDialog().apply {
+                this.app = app
+                this.foldersRepo = foldersRepo
+            }
+        }
+    }
+}

+ 4 - 4
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/RemoveAllAppsDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/RemoveAllAppsDialog.kt

@@ -1,12 +1,12 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.AlertDialog
 import android.app.Dialog
 import android.os.Bundle
 import androidx.fragment.app.DialogFragment
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.models.CustomiseAppsViewModel
-import com.sduduzog.slimlauncher.models.HomeApp
+import com.simplauncher.R
+import com.simplauncher.models.CustomiseAppsViewModel
+import com.simplauncher.models.HomeApp
 
 class RemoveAllAppsDialog : DialogFragment(){
 

+ 5 - 6
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/RenameAppDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/RenameAppDialog.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.Dialog
 import android.os.Bundle
@@ -7,10 +7,9 @@ import android.widget.EditText
 import android.widget.Toast
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.DialogFragment
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.models.HomeApp
-import com.sduduzog.slimlauncher.models.CustomiseAppsViewModel
-import kotlinx.android.synthetic.main.customise_apps_fragment.*
+import com.simplauncher.R
+import com.simplauncher.models.HomeApp
+import com.simplauncher.models.CustomiseAppsViewModel
 
 class RenameAppDialog : DialogFragment() {
 
@@ -18,7 +17,7 @@ class RenameAppDialog : DialogFragment() {
     private lateinit var model: CustomiseAppsViewModel
 
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
-        val view = LayoutInflater.from(context).inflate(R.layout.rename_dialog_edit_text, customise_apps_fragment, false)
+        val view = LayoutInflater.from(context).inflate(R.layout.rename_dialog_edit_text, null, false)
         val editText: EditText = view.findViewById(R.id.rename_editText)
         val appName: String = app.appNickname ?: app.appName
         editText.text.append(appName)

+ 5 - 6
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/RenameAppDisplayNameDialog.kt → app/src/main/java/com/simplauncher/ui/dialogs/RenameAppDisplayNameDialog.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.dialogs
+package com.simplauncher.ui.dialogs
 
 import android.app.Dialog
 import android.os.Bundle
@@ -7,17 +7,16 @@ import android.widget.EditText
 import android.widget.Toast
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.DialogFragment
-import com.jkuester.unlauncher.datastore.UnlauncherApp
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsRepository
-import kotlinx.android.synthetic.main.customise_apps_fragment.*
+import com.simplauncher.datastore.UnlauncherApp
+import com.simplauncher.R
+import com.simplauncher.datasource.apps.UnlauncherAppsRepository
 
 class RenameAppDisplayNameDialog : DialogFragment() {
     private lateinit var app: UnlauncherApp
     private lateinit var unlauncherAppsRepo: UnlauncherAppsRepository
 
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
-        val view = LayoutInflater.from(context).inflate(R.layout.rename_dialog_edit_text, customise_apps_fragment, false)
+        val view = LayoutInflater.from(context).inflate(R.layout.rename_dialog_edit_text, null, false)
         val editText: EditText = view.findViewById(R.id.rename_editText)
         val appName: String = app.displayName
         editText.text.append(appName)

+ 46 - 0
app/src/main/java/com/simplauncher/ui/dialogs/RenameFolderDialog.kt

@@ -0,0 +1,46 @@
+package com.simplauncher.ui.dialogs
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.widget.EditText
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import com.simplauncher.R
+import com.simplauncher.datasource.folders.AppDrawerFoldersRepository
+
+class RenameFolderDialog : DialogFragment() {
+
+    private lateinit var folderId: String
+    private lateinit var folderName: String
+    private lateinit var foldersRepo: AppDrawerFoldersRepository
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        val view = LayoutInflater.from(context).inflate(R.layout.rename_dialog_edit_text, null, false)
+        val editText: EditText = view.findViewById(R.id.rename_editText)
+        editText.text.append(folderName)
+        val builder = AlertDialog.Builder(requireContext())
+        builder.setTitle(R.string.rename_folder)
+        builder.setView(view)
+        builder.setPositiveButton(android.R.string.ok) { _, _ ->
+            val name = editText.text.toString().trim()
+            if (name.isNotEmpty()) foldersRepo.renameFolder(folderId, name)
+        }
+        builder.setNegativeButton(android.R.string.cancel, null)
+        return builder.create()
+    }
+
+    companion object {
+        fun getInstance(
+            folderId: String,
+            folderName: String,
+            foldersRepo: AppDrawerFoldersRepository
+        ): RenameFolderDialog {
+            return RenameFolderDialog().apply {
+                this.folderId = folderId
+                this.folderName = folderName
+                this.foldersRepo = foldersRepo
+            }
+        }
+    }
+}

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/ui/main/AnalogClockView.kt → app/src/main/java/com/simplauncher/ui/main/AnalogClockView.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.main
+package com.simplauncher.ui.main
 
 import android.content.Context
 import android.graphics.Canvas
@@ -8,7 +8,7 @@ import androidx.core.view.marginBottom
 import androidx.core.view.marginEnd
 import androidx.core.view.marginStart
 import androidx.core.view.marginTop
-import com.sduduzog.slimlauncher.R
+import com.simplauncher.R
 import java.util.Calendar
 import kotlin.math.max
 import kotlin.math.min

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/ui/main/BinaryClockView.kt → app/src/main/java/com/simplauncher/ui/main/BinaryClockView.kt

@@ -1,11 +1,11 @@
-package com.sduduzog.slimlauncher.ui.main
+package com.simplauncher.ui.main
 
 import android.content.Context
 import android.graphics.Canvas
 import android.graphics.Paint
 import android.graphics.RectF
 import android.util.AttributeSet
-import com.sduduzog.slimlauncher.R
+import com.simplauncher.R
 import java.util.Calendar
 
 class BinaryClockView(context: Context, attrs: AttributeSet)

+ 1 - 1
app/src/main/java/com/sduduzog/slimlauncher/ui/main/ClockView.kt → app/src/main/java/com/simplauncher/ui/main/ClockView.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.main
+package com.simplauncher.ui.main
 
 import android.content.Context
 import android.graphics.Canvas

+ 105 - 66
app/src/main/java/com/sduduzog/slimlauncher/ui/main/HomeFragment.kt → app/src/main/java/com/simplauncher/ui/main/HomeFragment.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.main
+package com.simplauncher.ui.main
 
 import android.app.Activity
 import android.content.ActivityNotFoundException
@@ -23,7 +23,10 @@ import android.view.View.OnClickListener
 import android.view.ViewGroup
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
+import android.widget.EditText
+import android.widget.ImageView
 import android.widget.PopupMenu
+import android.widget.TextView
 import android.widget.Toast
 import androidx.activity.result.ActivityResultLauncher
 import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
@@ -33,33 +36,23 @@ import androidx.fragment.app.viewModels
 import androidx.lifecycle.lifecycleScope
 import androidx.navigation.Navigation
 import androidx.recyclerview.widget.LinearLayoutManager
-import com.jkuester.unlauncher.datastore.ClockType
-import com.jkuester.unlauncher.datastore.SearchBarPosition
-import com.jkuester.unlauncher.datastore.UnlauncherApp
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.adapters.AppDrawerAdapter
-import com.sduduzog.slimlauncher.adapters.HomeAdapter
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
-import com.sduduzog.slimlauncher.models.HomeApp
-import com.sduduzog.slimlauncher.models.MainViewModel
-import com.sduduzog.slimlauncher.ui.dialogs.RenameAppDisplayNameDialog
-import com.sduduzog.slimlauncher.utils.BaseFragment
-import com.sduduzog.slimlauncher.utils.OnLaunchAppListener
-import com.sduduzog.slimlauncher.utils.isSystemApp
+import androidx.recyclerview.widget.RecyclerView
+import com.simplauncher.datastore.ClockType
+import com.simplauncher.datastore.SearchBarPosition
+import com.simplauncher.datastore.UnlauncherApp
+import com.simplauncher.R
+import com.simplauncher.adapters.AppDrawerAdapter
+import com.simplauncher.adapters.HomeAdapter
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
+import com.simplauncher.models.HomeApp
+import com.simplauncher.models.MainViewModel
+import com.simplauncher.ui.dialogs.ManageAppFoldersDialog
+import com.simplauncher.ui.dialogs.RenameAppDisplayNameDialog
+import com.simplauncher.utils.BaseFragment
+import com.simplauncher.utils.OnLaunchAppListener
+import com.simplauncher.utils.isSystemApp
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.home_fragment_content.app_drawer_edit_text
-import kotlinx.android.synthetic.main.home_fragment_content.app_drawer_fragment_list
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_analog_time
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_bin_time
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_call
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_camera
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_date
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_list
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_list_exp
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_options
-import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_time
-import kotlinx.android.synthetic.main.home_fragment_default.home_fragment
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import java.text.SimpleDateFormat
@@ -80,6 +73,20 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
     private lateinit var appDrawerAdapter: AppDrawerAdapter
     private lateinit var uninstallAppLauncher: ActivityResultLauncher<Intent>
 
+    // View references (content views come from <merge> include, so ViewBinding can't resolve them)
+    private var homeFragmentView: MotionLayout? = null
+    private var appDrawerEditText: EditText? = null
+    private var appDrawerFragmentList: RecyclerView? = null
+    private var homeFragmentTime: TextView? = null
+    private var homeFragmentDate: TextView? = null
+    private var homeFragmentAnalogTime: AnalogClockView? = null
+    private var homeFragmentBinTime: BinaryClockView? = null
+    private var homeFragmentList: RecyclerView? = null
+    private var homeFragmentListExp: RecyclerView? = null
+    private var homeFragmentCall: ImageView? = null
+    private var homeFragmentOptions: ImageView? = null
+    private var homeFragmentCamera: ImageView? = null
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         uninstallAppLauncher = registerForActivityResult(StartActivityForResult()) { refreshApps() }
@@ -93,15 +100,44 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
             SearchBarPosition.top -> R.layout.home_fragment_default
             else -> R.layout.home_fragment_default
         }
-        return inflater.inflate(layout, container, false)
+        val view = inflater.inflate(layout, container, false)
+        homeFragmentView = view as MotionLayout
+        appDrawerEditText = view.findViewById(R.id.app_drawer_edit_text)
+        appDrawerFragmentList = view.findViewById(R.id.app_drawer_fragment_list)
+        homeFragmentTime = view.findViewById(R.id.home_fragment_time)
+        homeFragmentDate = view.findViewById(R.id.home_fragment_date)
+        homeFragmentAnalogTime = view.findViewById(R.id.home_fragment_analog_time)
+        homeFragmentBinTime = view.findViewById(R.id.home_fragment_bin_time)
+        homeFragmentList = view.findViewById(R.id.home_fragment_list)
+        homeFragmentListExp = view.findViewById(R.id.home_fragment_list_exp)
+        homeFragmentCall = view.findViewById(R.id.home_fragment_call)
+        homeFragmentOptions = view.findViewById(R.id.home_fragment_options)
+        homeFragmentCamera = view.findViewById(R.id.home_fragment_camera)
+        return view
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        homeFragmentView = null
+        appDrawerEditText = null
+        appDrawerFragmentList = null
+        homeFragmentTime = null
+        homeFragmentDate = null
+        homeFragmentAnalogTime = null
+        homeFragmentBinTime = null
+        homeFragmentList = null
+        homeFragmentListExp = null
+        homeFragmentCall = null
+        homeFragmentOptions = null
+        homeFragmentCamera = null
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         val adapter1 = HomeAdapter(this, unlauncherDataSource)
         val adapter2 = HomeAdapter(this, unlauncherDataSource)
-        home_fragment_list.adapter = adapter1
-        home_fragment_list_exp.adapter = adapter2
+        homeFragmentList!!.adapter = adapter1
+        homeFragmentListExp!!.adapter = adapter2
 
         val unlauncherAppsRepo = unlauncherDataSource.unlauncherAppsRepo
 
@@ -129,16 +165,16 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
 
         setEventListeners()
 
-        app_drawer_fragment_list.adapter = appDrawerAdapter
+        appDrawerFragmentList!!.adapter = appDrawerAdapter
 
         unlauncherDataSource.corePreferencesRepo.liveData().observe(viewLifecycleOwner){ corePreferences ->
-            app_drawer_edit_text.visibility = if (corePreferences.showSearchBar) View.VISIBLE else View.GONE
+            appDrawerEditText!!.visibility = if (corePreferences.showSearchBar) View.VISIBLE else View.GONE
 
             val clockType = corePreferences.clockType
-            home_fragment_time.visibility = if(clockType == ClockType.digital) View.VISIBLE else View.GONE
-            home_fragment_analog_time.visibility = if(clockType == ClockType.analog) View.VISIBLE else View.GONE
-            home_fragment_bin_time.visibility = if(clockType == ClockType.binary) View.VISIBLE else View.GONE
-            home_fragment_date.visibility = if(clockType != ClockType.none) View.VISIBLE else View.GONE
+            homeFragmentTime!!.visibility = if(clockType == ClockType.digital) View.VISIBLE else View.GONE
+            homeFragmentAnalogTime!!.visibility = if(clockType == ClockType.analog) View.VISIBLE else View.GONE
+            homeFragmentBinTime!!.visibility = if(clockType == ClockType.binary) View.VISIBLE else View.GONE
+            homeFragmentDate!!.visibility = if(clockType != ClockType.none) View.VISIBLE else View.GONE
         }
     }
 
@@ -148,7 +184,7 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
         activity?.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIME_TICK))
     }
 
-    override fun getFragmentView(): ViewGroup = home_fragment
+    override fun getFragmentView(): ViewGroup = homeFragmentView!!
 
     override fun onResume() {
         super.onResume()
@@ -160,9 +196,9 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
         }
 
         // scroll back to the top if user returns to this fragment
-        val layoutManager = app_drawer_fragment_list.layoutManager as LinearLayoutManager
+        val layoutManager = appDrawerFragmentList!!.layoutManager as LinearLayoutManager
         if (layoutManager.findFirstCompletelyVisibleItemPosition() != 0) {
-            app_drawer_fragment_list.scrollToPosition(0)
+            appDrawerFragmentList!!.scrollToPosition(0)
         }
     }
 
@@ -192,11 +228,11 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
                 // Do nothing, we've failed :(
             }
         }
-        home_fragment_time.setOnClickListener(launchShowAlarms)
-        home_fragment_analog_time.setOnClickListener(launchShowAlarms)
-        home_fragment_bin_time.setOnClickListener(launchShowAlarms)
+        homeFragmentTime!!.setOnClickListener(launchShowAlarms)
+        homeFragmentAnalogTime!!.setOnClickListener(launchShowAlarms)
+        homeFragmentBinTime!!.setOnClickListener(launchShowAlarms)
 
-        home_fragment_date.setOnClickListener {
+        homeFragmentDate!!.setOnClickListener {
             try {
                 val builder = CalendarContract.CONTENT_URI.buildUpon().appendPath("time")
                 val intent = Intent(Intent.ACTION_VIEW, builder.build())
@@ -210,9 +246,9 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
         unlauncherDataSource.quickButtonPreferencesRepo.liveData()
             .observe(viewLifecycleOwner) { prefs ->
                 val leftButtonIcon = QuickButtonPreferencesRepository.RES_BY_ICON.getValue(prefs.leftButton.iconId)
-                home_fragment_call.setImageResource(leftButtonIcon)
+                homeFragmentCall!!.setImageResource(leftButtonIcon)
                 if (leftButtonIcon != R.drawable.ic_empty) {
-                    home_fragment_call.setOnClickListener { view ->
+                    homeFragmentCall!!.setOnClickListener { view ->
                         try {
                             val pm = context?.packageManager!!
                             val intent = Intent(Intent.ACTION_DIAL)
@@ -228,9 +264,9 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
                 }
 
                 val centerButtonIcon = QuickButtonPreferencesRepository.RES_BY_ICON.getValue(prefs.centerButton.iconId)
-                home_fragment_options.setImageResource(centerButtonIcon)
+                homeFragmentOptions!!.setImageResource(centerButtonIcon)
                 if (centerButtonIcon != R.drawable.ic_empty) {
-                    home_fragment_options.setOnClickListener(
+                    homeFragmentOptions!!.setOnClickListener(
                         Navigation.createNavigateOnClickListener(
                             R.id.action_homeFragment_to_optionsFragment
                         )
@@ -238,9 +274,9 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
                 }
 
                 val rightButtonIcon = QuickButtonPreferencesRepository.RES_BY_ICON.getValue(prefs.rightButton.iconId)
-                home_fragment_camera.setImageResource(rightButtonIcon)
+                homeFragmentCamera!!.setImageResource(rightButtonIcon)
                 if (rightButtonIcon != R.drawable.ic_empty) {
-                    home_fragment_camera.setOnClickListener {
+                    homeFragmentCamera!!.setOnClickListener {
                         try {
                             val intent = Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
                             launchActivity(it, intent)
@@ -251,20 +287,20 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
                 }
             }
 
-        app_drawer_edit_text.addTextChangedListener(appDrawerAdapter.searchBoxListener)
+        appDrawerEditText!!.addTextChangedListener(appDrawerAdapter.searchBoxListener)
 
-        app_drawer_edit_text.setOnEditorActionListener { _, actionId, _ ->
+        appDrawerEditText!!.setOnEditorActionListener { _, actionId, _ ->
                 if(actionId == EditorInfo.IME_ACTION_DONE && appDrawerAdapter.itemCount > 0) {
                     val firstApp = appDrawerAdapter.getFirstApp()
                     launchApp(firstApp.packageName, firstApp.className, firstApp.userSerial)
-                    home_fragment.transitionToStart()
+                    homeFragmentView!!.transitionToStart()
                     true
                 } else {
                     false
                 }
             }
 
-        home_fragment.setTransitionListener(object : TransitionListener {
+        homeFragmentView!!.setTransitionListener(object : TransitionListener {
             override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
                 val inputMethodManager = requireContext().getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
 
@@ -280,10 +316,10 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
                         // Check for preferences to open the keyboard
                         // only if the search field is shown
                         if (preferences.showSearchBar && preferences.activateKeyboardInDrawer) {
-                            app_drawer_edit_text.requestFocus()
+                            appDrawerEditText!!.requestFocus()
                             // show the keyboard and set focus to the EditText when swiping down
                             inputMethodManager.showSoftInput(
-                                app_drawer_edit_text,
+                                appDrawerEditText!!,
                                 InputMethodManager.SHOW_IMPLICIT
                             )
                         }
@@ -309,8 +345,8 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
         updateDate()
         when (unlauncherDataSource.corePreferencesRepo.get().clockType) {
             ClockType.digital -> updateClockDigital()
-            ClockType.analog -> home_fragment_analog_time.updateClock()
-            ClockType.binary -> home_fragment_bin_time.updateClock()
+            ClockType.analog -> homeFragmentAnalogTime!!.updateClock()
+            ClockType.binary -> homeFragmentBinTime!!.updateClock()
             else -> {}
         }
     }
@@ -323,12 +359,12 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
             2 -> SimpleDateFormat("h:mm aa", Locale.getDefault())
             else -> DateFormat.getTimeFormat(context)
         }
-        home_fragment_time.text = fWatchTime.format(Date())
+        homeFragmentTime!!.text = fWatchTime.format(Date())
     }
 
     private fun updateDate() {
         val fWatchDate = SimpleDateFormat(getString(R.string.main_date_format), Locale.getDefault())
-        home_fragment_date.text = fWatchDate.format(Date())
+        homeFragmentDate!!.text = fWatchDate.format(Date())
     }
 
     override fun onLaunch(app: HomeApp, view: View) {
@@ -336,12 +372,12 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
     }
 
     override fun onBack(): Boolean {
-        home_fragment.transitionToStart()
+        homeFragmentView!!.transitionToStart()
         return true
     }
 
     override fun onHome() {
-        home_fragment.transitionToStart()
+        homeFragmentView!!.transitionToStart()
     }
 
     inner class ClockReceiver : BroadcastReceiver() {
@@ -365,9 +401,9 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
     }
 
     private fun resetAppDrawerEditText() {
-        app_drawer_edit_text.clearComposingText()
-        app_drawer_edit_text.setText("")
-        app_drawer_edit_text.clearFocus()
+        appDrawerEditText!!.clearComposingText()
+        appDrawerEditText!!.setText("")
+        appDrawerEditText!!.clearFocus()
     }
 
     inner class AppDrawerListener {
@@ -390,11 +426,14 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
                     }
                     R.id.hide -> {
                         unlauncherDataSource.unlauncherAppsRepo.updateDisplayInDrawer(app, false)
-                        Toast.makeText(context, "Unhide under Unlauncher's Options > Customize Drawer > Visible Apps", Toast.LENGTH_LONG).show()
+                        Toast.makeText(context, "Unhide under Simplauncher's Options > Customize Drawer > Visible Apps", Toast.LENGTH_LONG).show()
                     }
                     R.id.rename -> {
                         RenameAppDisplayNameDialog.getInstance(app, unlauncherDataSource.unlauncherAppsRepo).show(childFragmentManager, "AppListAdapter")
                     }
+                    R.id.folders -> {
+                        ManageAppFoldersDialog.getInstance(app, unlauncherDataSource.appDrawerFoldersRepo).show(childFragmentManager, "ManageFolders")
+                    }
                     R.id.uninstall -> {
                         val intent = Intent(Intent.ACTION_DELETE)
                         intent.data = Uri.parse("package:" + app.packageName)
@@ -426,7 +465,7 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
 
         fun onAppClicked(app: UnlauncherApp) {
             launchApp(app.packageName, app.className, app.userSerial)
-            home_fragment.transitionToStart()
+            homeFragmentView!!.transitionToStart()
         }
     }
 }

+ 28 - 19
app/src/main/java/com/sduduzog/slimlauncher/ui/options/AddAppFragment.kt → app/src/main/java/com/simplauncher/ui/options/AddAppFragment.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.options
+package com.simplauncher.ui.options
 
 import android.os.Bundle
 import android.text.Editable
@@ -8,38 +8,47 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.viewModels
 import androidx.navigation.Navigation
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.adapters.AddAppAdapter
-import com.sduduzog.slimlauncher.data.model.App
-import com.sduduzog.slimlauncher.models.AddAppViewModel
-import com.sduduzog.slimlauncher.utils.BaseFragment
-import com.sduduzog.slimlauncher.utils.OnAppClickedListener
+import com.simplauncher.R
+import com.simplauncher.adapters.AddAppAdapter
+import com.simplauncher.data.model.App
+import com.simplauncher.databinding.AddAppFragmentBinding
+import com.simplauncher.models.AddAppViewModel
+import com.simplauncher.utils.BaseFragment
+import com.simplauncher.utils.OnAppClickedListener
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.add_app_fragment.*
 
 @AndroidEntryPoint
 class AddAppFragment : BaseFragment(), OnAppClickedListener {
 
-    override fun getFragmentView(): ViewGroup = add_app_fragment
+    private var _binding: AddAppFragmentBinding? = null
+    private val binding get() = _binding!!
 
-    private  val viewModel: AddAppViewModel by viewModels()
+    override fun getFragmentView(): ViewGroup = binding.addAppFragment
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-        return inflater.inflate(R.layout.add_app_fragment, container, false)
+    private val viewModel: AddAppViewModel by viewModels()
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        _binding = AddAppFragmentBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
     }
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
         val adapter = AddAppAdapter(this)
 
-        add_app_fragment_list.adapter = adapter
+        binding.addAppFragmentList.adapter = adapter
 
         viewModel.apps.observe(viewLifecycleOwner, {
             it?.let { apps ->
                 adapter.setItems(apps)
-                add_app_fragment_progress_bar.visibility = View.GONE
+                binding.addAppFragmentProgressBar.visibility = View.GONE
             } ?: run {
-                add_app_fragment_progress_bar.visibility = View.VISIBLE
+                binding.addAppFragmentProgressBar.visibility = View.VISIBLE
             }
         })
     }
@@ -48,12 +57,12 @@ class AddAppFragment : BaseFragment(), OnAppClickedListener {
         super.onResume()
         viewModel.setInstalledApps(getInstalledApps())
         viewModel.filterApps("")
-        add_app_fragment_edit_text.addTextChangedListener(onTextChangeListener)
+        binding.addAppFragmentEditText.addTextChangedListener(onTextChangeListener)
     }
 
     override fun onPause() {
         super.onPause()
-        add_app_fragment_edit_text?.removeTextChangedListener(onTextChangeListener)
+        binding.addAppFragmentEditText.removeTextChangedListener(onTextChangeListener)
     }
 
     private val onTextChangeListener: TextWatcher = object : TextWatcher {
@@ -73,6 +82,6 @@ class AddAppFragment : BaseFragment(), OnAppClickedListener {
 
     override fun onAppClicked(app: App) {
         viewModel.addAppToHomeScreen(app)
-        Navigation.findNavController(add_app_fragment).popBackStack()
+        Navigation.findNavController(binding.addAppFragment).popBackStack()
     }
-}
+}

+ 44 - 30
app/src/main/java/com/sduduzog/slimlauncher/ui/options/CustomiseAppsFragment.kt → app/src/main/java/com/simplauncher/ui/options/CustomiseAppsFragment.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.ui.options
+package com.simplauncher.ui.options
 
 import android.graphics.Canvas
 import android.os.Bundle
@@ -10,40 +10,40 @@ import androidx.fragment.app.viewModels
 import androidx.navigation.Navigation
 import androidx.recyclerview.widget.ItemTouchHelper
 import androidx.recyclerview.widget.RecyclerView
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.adapters.CustomAppsAdapter
-import com.sduduzog.slimlauncher.models.CustomiseAppsViewModel
-import com.sduduzog.slimlauncher.models.HomeApp
-import com.sduduzog.slimlauncher.ui.dialogs.RemoveAllAppsDialog
-import com.sduduzog.slimlauncher.ui.dialogs.RenameAppDialog
-import com.sduduzog.slimlauncher.utils.BaseFragment
-import com.sduduzog.slimlauncher.utils.OnItemActionListener
-import com.sduduzog.slimlauncher.utils.OnShitDoneToAppsListener
+import com.simplauncher.R
+import com.simplauncher.adapters.CustomAppsAdapter
+import com.simplauncher.databinding.CustomiseAppsFragmentBinding
+import com.simplauncher.models.CustomiseAppsViewModel
+import com.simplauncher.models.HomeApp
+import com.simplauncher.ui.dialogs.RemoveAllAppsDialog
+import com.simplauncher.ui.dialogs.RenameAppDialog
+import com.simplauncher.utils.BaseFragment
+import com.simplauncher.utils.OnItemActionListener
+import com.simplauncher.utils.OnShitDoneToAppsListener
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.customise_apps_fragment.customise_apps_fragment
-import kotlinx.android.synthetic.main.customise_apps_fragment.customise_apps_fragment_add
-import kotlinx.android.synthetic.main.customise_apps_fragment.customise_apps_fragment_back
-import kotlinx.android.synthetic.main.customise_apps_fragment.customise_apps_fragment_list
-import kotlinx.android.synthetic.main.customise_apps_fragment.customise_apps_fragment_remove_all
-
 
 @AndroidEntryPoint
 class CustomiseAppsFragment : BaseFragment(), OnShitDoneToAppsListener {
 
-    override fun getFragmentView(): ViewGroup = customise_apps_fragment
+    private var _binding: CustomiseAppsFragmentBinding? = null
+    private val binding get() = _binding!!
+
+    override fun getFragmentView(): ViewGroup = binding.customiseAppsFragment
 
     private val viewModel: CustomiseAppsViewModel by viewModels()
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-        return inflater.inflate(R.layout.customise_apps_fragment, container, false)
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        _binding = CustomiseAppsFragmentBinding.inflate(inflater, container, false)
+        return binding.root
     }
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-        customise_apps_fragment_back.setOnClickListener {
+        binding.customiseAppsFragmentBack.setOnClickListener {
             requireActivity().onBackPressedDispatcher.onBackPressed()
         }
     }
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         val adapter = CustomAppsAdapter(this)
@@ -51,20 +51,26 @@ class CustomiseAppsFragment : BaseFragment(), OnShitDoneToAppsListener {
         viewModel.apps.observe(viewLifecycleOwner, {
             it?.let { apps ->
                 adapter.setItems(apps)
-                customise_apps_fragment_add.visibility = if(apps.size < 6) View.VISIBLE else View.INVISIBLE
+                binding.customiseAppsFragmentAdd.visibility = if (apps.size < 6) View.VISIBLE else View.INVISIBLE
             } ?: adapter.setItems(listOf())
         })
-        customise_apps_fragment_remove_all.setOnClickListener {
+        binding.customiseAppsFragmentRemoveAll.setOnClickListener {
             RemoveAllAppsDialog.getInstance(viewModel.apps.value!!, viewModel).show(childFragmentManager, "REMOVE_APPS")
         }
 
-        customise_apps_fragment_list.adapter = adapter
+        binding.customiseAppsFragmentList.adapter = adapter
         val listener: OnItemActionListener = adapter
         val simpleItemTouchCallback = object : ItemTouchHelper.Callback() {
 
-            override fun onChildDraw(c: Canvas, recyclerView: RecyclerView,
-                                     viewHolder: RecyclerView.ViewHolder, dX: Float,
-                                     dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
+            override fun onChildDraw(
+                c: Canvas,
+                recyclerView: RecyclerView,
+                viewHolder: RecyclerView.ViewHolder,
+                dX: Float,
+                dY: Float,
+                actionState: Int,
+                isCurrentlyActive: Boolean
+            ) {
                 if (isCurrentlyActive) {
                     viewHolder.itemView.alpha = 0.5f
                 } else {
@@ -84,8 +90,11 @@ class CustomiseAppsFragment : BaseFragment(), OnShitDoneToAppsListener {
                 return makeMovementFlags(dragFlags, swipeFlags)
             }
 
-            override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
-                                target: RecyclerView.ViewHolder): Boolean {
+            override fun onMove(
+                recyclerView: RecyclerView,
+                viewHolder: RecyclerView.ViewHolder,
+                target: RecyclerView.ViewHolder
+            ): Boolean {
                 return listener.onViewMoved(viewHolder.adapterPosition, target.adapterPosition)
             }
 
@@ -98,11 +107,11 @@ class CustomiseAppsFragment : BaseFragment(), OnShitDoneToAppsListener {
 
         val itemTouchHelper = ItemTouchHelper(simpleItemTouchCallback)
 
-        itemTouchHelper.attachToRecyclerView(customise_apps_fragment_list)
+        itemTouchHelper.attachToRecyclerView(binding.customiseAppsFragmentList)
 
         adapter.setItemTouchHelper(itemTouchHelper)
 
-        customise_apps_fragment_add.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_customiseAppsFragment_to_addAppFragment))
+        binding.customiseAppsFragmentAdd.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_customiseAppsFragment_to_addAppFragment))
     }
 
     private fun showPopupMenu(view: View): PopupMenu {
@@ -133,4 +142,9 @@ class CustomiseAppsFragment : BaseFragment(), OnShitDoneToAppsListener {
             true
         }
     }
+
+    override fun onDestroyView() {
+        _binding = null
+        super.onDestroyView()
+    }
 }

+ 54 - 0
app/src/main/java/com/simplauncher/ui/options/CustomizeAppDrawerAppListFragment.kt

@@ -0,0 +1,54 @@
+package com.simplauncher.ui.options
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.simplauncher.adapters.CustomizeAppDrawerAppsAdapter
+import com.simplauncher.databinding.CustomizeAppDrawerAppListFragmentBinding
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.utils.BaseFragment
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class CustomizeAppDrawerAppListFragment : BaseFragment() {
+    @Inject
+    lateinit var unlauncherDataSource: UnlauncherDataSource
+
+    private var _binding: CustomizeAppDrawerAppListFragmentBinding? = null
+    private val binding get() = _binding!!
+
+    override fun getFragmentView(): ViewGroup = binding.customizeAppDrawerFragment
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        _binding = CustomizeAppDrawerAppListFragmentBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+        val unlauncherAppsRepo = unlauncherDataSource.unlauncherAppsRepo
+        binding.customizeAppDrawerFragmentAppList.adapter =
+            CustomizeAppDrawerAppsAdapter(viewLifecycleOwner, unlauncherAppsRepo)
+        unlauncherAppsRepo.liveData().observe(viewLifecycleOwner) {
+            it?.let {
+                binding.customizeAppDrawerFragmentAppProgressBar.visibility = View.GONE
+            } ?: run {
+                binding.customizeAppDrawerFragmentAppProgressBar.visibility = View.VISIBLE
+            }
+        }
+        binding.customizeAppDrawerFragmentBack.setOnClickListener {
+            requireActivity().onBackPressedDispatcher.onBackPressed()
+        }
+    }
+
+    override fun onDestroyView() {
+        _binding = null
+        super.onDestroyView()
+    }
+}

+ 30 - 20
app/src/main/java/com/sduduzog/slimlauncher/ui/options/CustomizeAppDrawerFragment.kt → app/src/main/java/com/simplauncher/ui/options/CustomizeAppDrawerFragment.kt

@@ -1,21 +1,17 @@
-package com.sduduzog.slimlauncher.ui.options
+package com.simplauncher.ui.options
 
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.navigation.Navigation
-import com.jkuester.unlauncher.datastore.SearchBarPosition
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.utils.BaseFragment
-import com.sduduzog.slimlauncher.utils.createTitleAndSubtitleText
+import com.simplauncher.datastore.SearchBarPosition
+import com.simplauncher.R
+import com.simplauncher.databinding.CustomizeAppDrawerFragmentBinding
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.utils.BaseFragment
+import com.simplauncher.utils.createTitleAndSubtitleText
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment.customize_app_drawer_fragment
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment.customize_app_drawer_fragment_back
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment.customize_app_drawer_fragment_search_options
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment.customize_app_drawer_fragment_show_headings_switch
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment.customize_app_drawer_fragment_visible_apps
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -24,30 +20,44 @@ class CustomizeAppDrawerFragment : BaseFragment() {
     @Inject
     lateinit var unlauncherDataSource: UnlauncherDataSource
 
-    override fun getFragmentView(): ViewGroup = customize_app_drawer_fragment
+    private var _binding: CustomizeAppDrawerFragmentBinding? = null
+    private val binding get() = _binding!!
+
+    override fun getFragmentView(): ViewGroup = binding.customizeAppDrawerFragment
 
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?
-    ): View? = inflater.inflate(R.layout.customize_app_drawer_fragment, container, false)
+    ): View {
+        _binding = CustomizeAppDrawerFragmentBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        customize_app_drawer_fragment_visible_apps
+        binding.customizeAppDrawerFragmentVisibleApps
             .setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_customiseAppDrawerFragment_to_customiseAppDrawerAppListFragment))
 
-        customize_app_drawer_fragment_back.setOnClickListener{
+        binding.customizeAppDrawerFragmentBack.setOnClickListener{
             requireActivity().onBackPressedDispatcher.onBackPressed()
         }
         
         setupSearchFieldOptionsButton()
         setupHeadingSwitch()
+
+        binding.customizeAppDrawerFragmentFolders
+            .setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_customiseAppDrawerFragment_to_customizeFoldersFragment))
     }
 
     private fun setupSearchFieldOptionsButton() {
-        customize_app_drawer_fragment_search_options.setOnClickListener(
+        binding.customizeAppDrawerFragmentSearchOptions.setOnClickListener(
             Navigation.createNavigateOnClickListener(R.id.action_customiseAppDrawerFragment_to_customizeSearchFieldFragment)
         )
         val preferencesRepository = unlauncherDataSource.corePreferencesRepo
@@ -72,20 +82,20 @@ class CustomizeAppDrawerFragment : BaseFragment() {
                 getText(R.string.customize_app_drawer_fragment_search_field_options_subtitle_status_hidden)
             }
 
-            customize_app_drawer_fragment_search_options.text =
+            binding.customizeAppDrawerFragmentSearchOptions.text =
                 createTitleAndSubtitleText(requireContext(), title, subtitle)
         }
     }
 
     private fun setupHeadingSwitch() {
         val prefsRepo = unlauncherDataSource.corePreferencesRepo
-        customize_app_drawer_fragment_show_headings_switch.setOnCheckedChangeListener { _, checked ->
+        binding.customizeAppDrawerFragmentShowHeadingsSwitch.setOnCheckedChangeListener { _, checked ->
             prefsRepo.updateShowDrawerHeadings(checked)
         }
         prefsRepo.liveData().observe(viewLifecycleOwner) {
-            customize_app_drawer_fragment_show_headings_switch.isChecked = it.showDrawerHeadings
+            binding.customizeAppDrawerFragmentShowHeadingsSwitch.isChecked = it.showDrawerHeadings
         }
-        customize_app_drawer_fragment_show_headings_switch.text =
+        binding.customizeAppDrawerFragmentShowHeadingsSwitch.text =
             createTitleAndSubtitleText(
                     requireContext(), R.string.customize_app_drawer_fragment_show_headings,
                     R.string.customize_app_drawer_fragment_show_headings_subtitle

+ 96 - 0
app/src/main/java/com/simplauncher/ui/options/CustomizeFolderDetailFragment.kt

@@ -0,0 +1,96 @@
+package com.simplauncher.ui.options
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.PopupMenu
+import com.simplauncher.R
+import com.simplauncher.adapters.CustomizeFolderAppsAdapter
+import com.simplauncher.datastore.UnlauncherApp
+import com.simplauncher.databinding.CustomizeFolderDetailFragmentBinding
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.ui.dialogs.RenameFolderDialog
+import com.simplauncher.utils.BaseFragment
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class CustomizeFolderDetailFragment : BaseFragment() {
+
+    @Inject
+    lateinit var unlauncherDataSource: UnlauncherDataSource
+
+    private var _binding: CustomizeFolderDetailFragmentBinding? = null
+    private val binding get() = _binding!!
+
+    private var allApps: List<UnlauncherApp> = emptyList()
+    private var folderPackageKeys: Set<String> = emptySet()
+
+    override fun getFragmentView(): ViewGroup = binding.customizeFolderDetailFragment
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        _binding = CustomizeFolderDetailFragmentBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val folderId = arguments?.getString("folderId") ?: return
+        val foldersRepo = unlauncherDataSource.appDrawerFoldersRepo
+
+        val adapter = CustomizeFolderAppsAdapter { app, checked ->
+            if (checked) {
+                foldersRepo.addAppToFolder(folderId, app)
+            } else {
+                foldersRepo.removeAppFromFolder(folderId, app)
+            }
+        }
+
+        binding.customizeFolderDetailList.adapter = adapter
+
+        unlauncherDataSource.unlauncherAppsRepo.liveData().observe(viewLifecycleOwner) { unlauncherApps ->
+            allApps = unlauncherApps.appsList
+            adapter.setItems(allApps, folderPackageKeys)
+        }
+
+        foldersRepo.liveData().observe(viewLifecycleOwner) { folders ->
+            val folder = folders.foldersList.firstOrNull { it.id == folderId } ?: return@observe
+            binding.customizeFolderDetailTitle.text = folder.name
+            folderPackageKeys = folder.appsList.map { "${it.packageName}/${it.className}" }.toSet()
+            adapter.setItems(allApps, folderPackageKeys)
+        }
+
+        binding.customizeFolderDetailBack.setOnClickListener {
+            requireActivity().onBackPressedDispatcher.onBackPressed()
+        }
+
+        binding.customizeFolderDetailMenu.setOnClickListener { menuView ->
+            val popup = PopupMenu(requireContext(), menuView)
+            popup.menuInflater.inflate(R.menu.folder_detail_menu, popup.menu)
+            popup.setForceShowIcon(true)
+            popup.setOnMenuItemClickListener { menuItem ->
+                when (menuItem.itemId) {
+                    R.id.folder_menu_rename -> {
+                        val folder = foldersRepo.get().foldersList.firstOrNull { it.id == folderId }
+                            ?: return@setOnMenuItemClickListener true
+                        RenameFolderDialog.getInstance(folderId, folder.name, foldersRepo)
+                            .show(childFragmentManager, "RENAME_FOLDER")
+                    }
+                    R.id.folder_menu_delete -> {
+                        foldersRepo.deleteFolder(folderId)
+                        requireActivity().onBackPressedDispatcher.onBackPressed()
+                    }
+                }
+                true
+            }
+            popup.show()
+        }
+    }
+}

+ 63 - 0
app/src/main/java/com/simplauncher/ui/options/CustomizeFoldersFragment.kt

@@ -0,0 +1,63 @@
+package com.simplauncher.ui.options
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.Navigation
+import com.simplauncher.R
+import com.simplauncher.adapters.CustomizeFoldersAdapter
+import com.simplauncher.databinding.CustomizeFoldersFragmentBinding
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.ui.dialogs.CreateFolderDialog
+import com.simplauncher.utils.BaseFragment
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class CustomizeFoldersFragment : BaseFragment() {
+
+    @Inject
+    lateinit var unlauncherDataSource: UnlauncherDataSource
+
+    private var _binding: CustomizeFoldersFragmentBinding? = null
+    private val binding get() = _binding!!
+
+    override fun getFragmentView(): ViewGroup = binding.customizeFoldersFragment
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        _binding = CustomizeFoldersFragmentBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val foldersRepo = unlauncherDataSource.appDrawerFoldersRepo
+        val adapter = CustomizeFoldersAdapter { folder ->
+            val bundle = Bundle().apply { putString("folderId", folder.id) }
+            Navigation.findNavController(requireView())
+                .navigate(R.id.action_customizeFoldersFragment_to_customizeFolderDetailFragment, bundle)
+        }
+
+        binding.customizeFoldersFragmentList.adapter = adapter
+
+        foldersRepo.liveData().observe(viewLifecycleOwner) { folders ->
+            adapter.setFolders(folders.foldersList)
+        }
+
+        binding.customizeFoldersFragmentBack.setOnClickListener {
+            requireActivity().onBackPressedDispatcher.onBackPressed()
+        }
+
+        binding.customizeFoldersFragmentCreate.setOnClickListener {
+            CreateFolderDialog.getInstance(foldersRepo)
+                .show(childFragmentManager, "CREATE_FOLDER")
+        }
+    }
+}

+ 26 - 20
app/src/main/java/com/sduduzog/slimlauncher/ui/options/CustomizeQuickButtonsFragment.kt → app/src/main/java/com/simplauncher/ui/options/CustomizeQuickButtonsFragment.kt

@@ -1,20 +1,15 @@
-package com.sduduzog.slimlauncher.ui.options
+package com.simplauncher.ui.options
 
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
-import com.sduduzog.slimlauncher.ui.dialogs.ChooseQuickButtonDialog
-import com.sduduzog.slimlauncher.utils.BaseFragment
+import com.simplauncher.databinding.CustomizeQuickButtonsFragmentBinding
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.datasource.quickbuttonprefs.QuickButtonPreferencesRepository
+import com.simplauncher.ui.dialogs.ChooseQuickButtonDialog
+import com.simplauncher.utils.BaseFragment
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.customize_quick_buttons_fragment.customize_quick_buttons_fragment
-import kotlinx.android.synthetic.main.customize_quick_buttons_fragment.customize_quick_buttons_fragment_back
-import kotlinx.android.synthetic.main.customize_quick_buttons_fragment.customize_quick_buttons_fragment_center
-import kotlinx.android.synthetic.main.customize_quick_buttons_fragment.customize_quick_buttons_fragment_left
-import kotlinx.android.synthetic.main.customize_quick_buttons_fragment.customize_quick_buttons_fragment_right
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -22,47 +17,58 @@ class CustomizeQuickButtonsFragment : BaseFragment() {
     @Inject
     lateinit var unlauncherDataSource: UnlauncherDataSource
 
-    override fun getFragmentView(): ViewGroup = customize_quick_buttons_fragment
+    private var _binding: CustomizeQuickButtonsFragmentBinding? = null
+    private val binding get() = _binding!!
+
+    override fun getFragmentView(): ViewGroup = binding.customizeQuickButtonsFragment
 
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?
-    ): View? = inflater.inflate(R.layout.customize_quick_buttons_fragment, container, false)
+    ): View {
+        _binding = CustomizeQuickButtonsFragmentBinding.inflate(inflater, container, false)
+        return binding.root
+    }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         val prefsRepo = unlauncherDataSource.quickButtonPreferencesRepo
 
         prefsRepo.liveData().observe(viewLifecycleOwner) { prefs ->
-            customize_quick_buttons_fragment_left
+            binding.customizeQuickButtonsFragmentLeft
                 .setImageResource(QuickButtonPreferencesRepository.RES_BY_ICON.getValue(prefs.leftButton.iconId))
-            customize_quick_buttons_fragment_center
+            binding.customizeQuickButtonsFragmentCenter
                 .setImageResource(QuickButtonPreferencesRepository.RES_BY_ICON.getValue(prefs.centerButton.iconId))
-            customize_quick_buttons_fragment_right
+            binding.customizeQuickButtonsFragmentRight
                 .setImageResource(QuickButtonPreferencesRepository.RES_BY_ICON.getValue(prefs.rightButton.iconId))
         }
 
-        customize_quick_buttons_fragment_back.setOnClickListener {
+        binding.customizeQuickButtonsFragmentBack.setOnClickListener {
             requireActivity().onBackPressedDispatcher.onBackPressed()
         }
-        customize_quick_buttons_fragment_left.setOnClickListener {
+        binding.customizeQuickButtonsFragmentLeft.setOnClickListener {
             ChooseQuickButtonDialog(
                 prefsRepo,
                 QuickButtonPreferencesRepository.IC_CALL
             ).showNow(childFragmentManager, "QUICK_BUTTON_CHOOSER")
         }
-        customize_quick_buttons_fragment_center.setOnClickListener {
+        binding.customizeQuickButtonsFragmentCenter.setOnClickListener {
             ChooseQuickButtonDialog(
                 prefsRepo,
                 QuickButtonPreferencesRepository.IC_COG
             ).showNow(childFragmentManager, "QUICK_BUTTON_CHOOSER")
         }
-        customize_quick_buttons_fragment_right.setOnClickListener {
+        binding.customizeQuickButtonsFragmentRight.setOnClickListener {
             ChooseQuickButtonDialog(
                 prefsRepo,
                 QuickButtonPreferencesRepository.IC_PHOTO_CAMERA
             ).showNow(childFragmentManager, "QUICK_BUTTON_CHOOSER")
         }
     }
+
+    override fun onDestroyView() {
+        _binding = null
+        super.onDestroyView()
+    }
 }

+ 38 - 38
app/src/main/java/com/sduduzog/slimlauncher/ui/options/CustomizeSearchFieldFragment.kt → app/src/main/java/com/simplauncher/ui/options/CustomizeSearchFieldFragment.kt

@@ -1,23 +1,16 @@
-package com.sduduzog.slimlauncher.ui.options
+package com.simplauncher.ui.options
 
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.ui.dialogs.ChooseSearchBarPositionDialog
-import com.sduduzog.slimlauncher.utils.BaseFragment
-import com.sduduzog.slimlauncher.utils.createTitleAndSubtitleText
+import com.simplauncher.R
+import com.simplauncher.databinding.CustomizeAppDrawerFragmentSearchFieldOptionsBinding
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.ui.dialogs.ChooseSearchBarPositionDialog
+import com.simplauncher.utils.BaseFragment
+import com.simplauncher.utils.createTitleAndSubtitleText
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment_search_field_options.customise_apps_fragment_back
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment_search_field_options.customize_app_drawer_fragment_search_field_options
-
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment_search_field_options.customize_app_drawer_fragment_search_field_position
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment_search_field_options.customize_app_drawer_fragment_show_search_field_switch
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment_search_field_options.customize_app_drawer_open_keyboard_switch
-import kotlinx.android.synthetic.main.customize_app_drawer_fragment_search_field_options.customize_app_drawer_search_all_switch
-
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -26,22 +19,24 @@ class CustomizeSearchFieldFragment : BaseFragment() {
     @Inject
     lateinit var unlauncherDataSource: UnlauncherDataSource
 
-    override fun getFragmentView(): ViewGroup = customize_app_drawer_fragment_search_field_options
+    private var _binding: CustomizeAppDrawerFragmentSearchFieldOptionsBinding? = null
+    private val binding get() = _binding!!
+
+    override fun getFragmentView(): ViewGroup = binding.customizeAppDrawerFragmentSearchFieldOptions
 
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?
-    ): View? = inflater.inflate(
-        R.layout.customize_app_drawer_fragment_search_field_options,
-        container,
-        false
-    )
+    ): View {
+        _binding = CustomizeAppDrawerFragmentSearchFieldOptionsBinding.inflate(inflater, container, false)
+        return binding.root
+    }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        customise_apps_fragment_back.setOnClickListener{
+        binding.customiseAppsFragmentBack.setOnClickListener {
             requireActivity().onBackPressedDispatcher.onBackPressed()
         }
 
@@ -53,25 +48,25 @@ class CustomizeSearchFieldFragment : BaseFragment() {
 
     private fun setupShowSearchBarSwitch() {
         val prefsRepo = unlauncherDataSource.corePreferencesRepo
-        customize_app_drawer_fragment_show_search_field_switch.setOnCheckedChangeListener { _, checked ->
+        binding.customizeAppDrawerFragmentShowSearchFieldSwitch.setOnCheckedChangeListener { _, checked ->
             prefsRepo.updateShowSearchBar(checked)
             enableSearchBarOptions(checked)
         }
         prefsRepo.liveData().observe(viewLifecycleOwner) {
             val checked = it.showSearchBar
-            customize_app_drawer_fragment_show_search_field_switch.isChecked = checked
+            binding.customizeAppDrawerFragmentShowSearchFieldSwitch.isChecked = checked
             enableSearchBarOptions(checked)
         }
     }
 
     private fun enableSearchBarOptions(enabled: Boolean) {
-        customize_app_drawer_fragment_search_field_position.isEnabled = enabled
-        customize_app_drawer_open_keyboard_switch.isEnabled = enabled
+        binding.customizeAppDrawerFragmentSearchFieldPosition.isEnabled = enabled
+        binding.customizeAppDrawerOpenKeyboardSwitch.isEnabled = enabled
     }
 
     private fun setupSearchBarPositionOption() {
         val prefRepo = unlauncherDataSource.corePreferencesRepo
-        customize_app_drawer_fragment_search_field_position.setOnClickListener {
+        binding.customizeAppDrawerFragmentSearchFieldPosition.setOnClickListener {
             val positionDialog = ChooseSearchBarPositionDialog.getSearchBarPositionChooser()
             positionDialog.showNow(childFragmentManager, "POSITION_CHOOSER")
         }
@@ -79,20 +74,20 @@ class CustomizeSearchFieldFragment : BaseFragment() {
             val position = it.searchBarPosition.number
             val title = getText(R.string.customize_app_drawer_fragment_search_bar_position)
             val subtitle = resources.getTextArray(R.array.search_bar_position_array)[position]
-            customize_app_drawer_fragment_search_field_position.text =
+            binding.customizeAppDrawerFragmentSearchFieldPosition.text =
                 createTitleAndSubtitleText(requireContext(), title, subtitle)
         }
     }
 
     private fun setupKeyboardSwitch() {
         val prefsRepo = unlauncherDataSource.corePreferencesRepo
-        customize_app_drawer_open_keyboard_switch.setOnCheckedChangeListener { _, checked ->
+        binding.customizeAppDrawerOpenKeyboardSwitch.setOnCheckedChangeListener { _, checked ->
             prefsRepo.updateActivateKeyboardInDrawer(checked)
         }
         prefsRepo.liveData().observe(viewLifecycleOwner) {
-            customize_app_drawer_open_keyboard_switch.isChecked = it.activateKeyboardInDrawer
+            binding.customizeAppDrawerOpenKeyboardSwitch.isChecked = it.activateKeyboardInDrawer
         }
-        customize_app_drawer_open_keyboard_switch.text =
+        binding.customizeAppDrawerOpenKeyboardSwitch.text =
             createTitleAndSubtitleText(
                 requireContext(), R.string.customize_app_drawer_fragment_open_keyboard,
                 R.string.customize_app_drawer_fragment_open_keyboard_subtitle
@@ -101,16 +96,21 @@ class CustomizeSearchFieldFragment : BaseFragment() {
 
     private fun setupSearchAllAppsSwitch() {
         val prefsRepo = unlauncherDataSource.corePreferencesRepo
-        customize_app_drawer_search_all_switch.setOnCheckedChangeListener { _, checked ->
+        binding.customizeAppDrawerSearchAllSwitch.setOnCheckedChangeListener { _, checked ->
             prefsRepo.updateSearchAllAppsInDrawer(checked)
         }
         prefsRepo.liveData().observe(viewLifecycleOwner) {
-            customize_app_drawer_search_all_switch.isChecked = it.searchAllAppsInDrawer
+            binding.customizeAppDrawerSearchAllSwitch.isChecked = it.searchAllAppsInDrawer
         }
-        customize_app_drawer_search_all_switch.text =
-                createTitleAndSubtitleText(
-                        requireContext(), R.string.customize_app_drawer_fragment_search_all,
-                        R.string.customize_app_drawer_fragment_search_all_subtitle
-                )
+        binding.customizeAppDrawerSearchAllSwitch.text =
+            createTitleAndSubtitleText(
+                requireContext(), R.string.customize_app_drawer_fragment_search_all,
+                R.string.customize_app_drawer_fragment_search_all_subtitle
+            )
+    }
+
+    override fun onDestroyView() {
+        _binding = null
+        super.onDestroyView()
     }
-}
+}

+ 39 - 41
app/src/main/java/com/sduduzog/slimlauncher/ui/options/OptionsFragment.kt → app/src/main/java/com/simplauncher/ui/options/OptionsFragment.kt

@@ -1,28 +1,25 @@
-package com.sduduzog.slimlauncher.ui.options
+package com.simplauncher.ui.options
 
 import android.content.Context.MODE_PRIVATE
 import android.content.Intent
 import android.os.Bundle
 import android.provider.Settings
-import android.text.SpannableStringBuilder
-import android.text.Spanned
-import android.text.style.TextAppearanceSpan
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.core.content.edit
 import androidx.navigation.Navigation
-import com.sduduzog.slimlauncher.R
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
-import com.sduduzog.slimlauncher.ui.dialogs.ChangeThemeDialog
-import com.sduduzog.slimlauncher.ui.dialogs.ChooseAlignmentDialog
-import com.sduduzog.slimlauncher.ui.dialogs.ChooseClockTypeDialog
-import com.sduduzog.slimlauncher.ui.dialogs.ChooseTimeFormatDialog
-import com.sduduzog.slimlauncher.utils.BaseFragment
-import com.sduduzog.slimlauncher.utils.createTitleAndSubtitleText
-import com.sduduzog.slimlauncher.utils.isActivityDefaultLauncher
+import com.simplauncher.R
+import com.simplauncher.databinding.OptionsFragmentBinding
+import com.simplauncher.datasource.UnlauncherDataSource
+import com.simplauncher.ui.dialogs.ChangeThemeDialog
+import com.simplauncher.ui.dialogs.ChooseAlignmentDialog
+import com.simplauncher.ui.dialogs.ChooseClockTypeDialog
+import com.simplauncher.ui.dialogs.ChooseTimeFormatDialog
+import com.simplauncher.utils.BaseFragment
+import com.simplauncher.utils.createTitleAndSubtitleText
+import com.simplauncher.utils.isActivityDefaultLauncher
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.options_fragment.*
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -30,58 +27,65 @@ class OptionsFragment : BaseFragment() {
     @Inject
     lateinit var unlauncherDataSource: UnlauncherDataSource
 
-    override fun getFragmentView(): ViewGroup = options_fragment
+    private var _binding: OptionsFragmentBinding? = null
+    private val binding get() = _binding!!
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-        return inflater.inflate(R.layout.options_fragment, container, false)
+    override fun getFragmentView(): ViewGroup = binding.optionsFragment
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        _binding = OptionsFragmentBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
     }
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         super.onActivityCreated(savedInstanceState)
-        options_fragment_device_settings.setOnClickListener {
+        binding.optionsFragmentDeviceSettings.setOnClickListener {
             val intent = Intent(Settings.ACTION_SETTINGS)
             launchActivity(it, intent)
         }
-        options_fragment_back.setOnClickListener{
+        binding.optionsFragmentBack.setOnClickListener{
             requireActivity().onBackPressedDispatcher.onBackPressed()
         }
-        options_fragment_device_settings.setOnLongClickListener {
+        binding.optionsFragmentDeviceSettings.setOnLongClickListener {
             val intent = Intent(Settings.ACTION_HOME_SETTINGS)
             launchActivity(it, intent)
             true
         }
-        options_fragment_change_theme.setOnClickListener {
+        binding.optionsFragmentChangeTheme.setOnClickListener {
             val changeThemeDialog = ChangeThemeDialog.getThemeChooser()
             changeThemeDialog.showNow(childFragmentManager, "THEME_CHOOSER")
         }
-        options_fragment_choose_time_format.setOnClickListener {
+        binding.optionsFragmentChooseTimeFormat.setOnClickListener {
             val chooseTimeFormatDialog = ChooseTimeFormatDialog.getInstance()
             chooseTimeFormatDialog.showNow(childFragmentManager, "TIME_FORMAT_CHOOSER")
         }
-        options_fragment_choose_clock_type.setOnClickListener {
+        binding.optionsFragmentChooseClockType.setOnClickListener {
             val chooseClockTypeDialog = ChooseClockTypeDialog.getInstance()
             chooseClockTypeDialog.showNow(childFragmentManager, "CLOCK_TYPE_CHOOSER")
         }
-        options_fragment_choose_alignment.setOnClickListener {
+        binding.optionsFragmentChooseAlignment.setOnClickListener {
             val chooseAlignmentDialog = ChooseAlignmentDialog.getInstance()
             chooseAlignmentDialog.showNow(childFragmentManager, "ALIGNMENT_CHOOSER")
         }
-        options_fragment_toggle_status_bar.setOnClickListener {
+        binding.optionsFragmentToggleStatusBar.setOnClickListener {
             val settings = requireContext().getSharedPreferences(getString(R.string.prefs_settings), MODE_PRIVATE)
             val isHidden = settings.getBoolean(getString(R.string.prefs_settings_key_toggle_status_bar), false)
             settings.edit {
                 putBoolean(getString(R.string.prefs_settings_key_toggle_status_bar), !isHidden)
             }
         }
-        options_fragment_customise_apps.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_optionsFragment_to_customiseAppsFragment))
-        options_fragment_customize_quick_buttons.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_optionsFragment_to_customiseQuickButtonsFragment))
-        options_fragment_customize_app_drawer.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_optionsFragment_to_customiseAppDrawerFragment))
+        binding.optionsFragmentCustomiseApps.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_optionsFragment_to_customiseAppsFragment))
+        binding.optionsFragmentCustomizeQuickButtons.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_optionsFragment_to_customiseQuickButtonsFragment))
+        binding.optionsFragmentCustomizeAppDrawer.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_optionsFragment_to_customiseAppDrawerFragment))
     }
 
     override fun onStart() {
         super.onStart()
-        // setting up the switch text, since changing the default launcher re-starts the activity
-        // this should able to adapt to it.
         setupAutomaticDeviceWallpaperSwitch()
     }
 
@@ -89,34 +93,28 @@ class OptionsFragment : BaseFragment() {
         val prefsRepo = unlauncherDataSource.corePreferencesRepo
         val appIsDefaultLauncher = isActivityDefaultLauncher(activity)
         setupDeviceWallpaperSwitchText(appIsDefaultLauncher)
-        options_fragment_auto_device_theme_wallpaper.isEnabled = appIsDefaultLauncher
+        binding.optionsFragmentAutoDeviceThemeWallpaper.isEnabled = appIsDefaultLauncher
 
         prefsRepo.liveData().observe(viewLifecycleOwner) {
-            // always uncheck once app isn't default launcher
-            options_fragment_auto_device_theme_wallpaper.isChecked = appIsDefaultLauncher && !it.keepDeviceWallpaper
+            binding.optionsFragmentAutoDeviceThemeWallpaper.isChecked = appIsDefaultLauncher && !it.keepDeviceWallpaper
         }
-        options_fragment_auto_device_theme_wallpaper.setOnCheckedChangeListener { _, checked ->
+        binding.optionsFragmentAutoDeviceThemeWallpaper.setOnCheckedChangeListener { _, checked ->
             prefsRepo.updateKeepDeviceWallpaper(!checked)
         }
     }
 
-    /**
-     * Adds a hint text underneath the default text when app is not the default launcher.
-     */
     private fun setupDeviceWallpaperSwitchText(appIsDefaultLauncher: Boolean) {
         val text = if (appIsDefaultLauncher) {
             getText(R.string.customize_app_drawer_fragment_auto_theme_wallpaper_text)
         } else {
             buildSwitchTextWithHint()
         }
-        options_fragment_auto_device_theme_wallpaper.text = text
+        binding.optionsFragmentAutoDeviceThemeWallpaper.text = text
     }
 
     private fun buildSwitchTextWithHint(): CharSequence {
         val titleText = getText(R.string.customize_app_drawer_fragment_auto_theme_wallpaper_text)
-        // have a title text and a subtitle text to indicate that adapting the
-        // wallpaper can only be done when app it the default launcher
         val subTitleText = getText(R.string.customize_app_drawer_fragment_auto_theme_wallpaper_subtext_no_default_launcher)
         return createTitleAndSubtitleText(requireContext(), titleText, subTitleText)
     }
-}
+}

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/utils/BaseFragment.kt → app/src/main/java/com/simplauncher/utils/BaseFragment.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 import android.content.Context
 import android.content.Intent
@@ -9,8 +9,8 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.core.app.ActivityOptionsCompat
 import androidx.fragment.app.Fragment
-import com.sduduzog.slimlauncher.BuildConfig
-import com.sduduzog.slimlauncher.data.model.App
+import com.simplauncher.BuildConfig
+import com.simplauncher.data.model.App
 import javax.inject.Inject
 
 abstract class BaseFragment : Fragment(), ISubscriber {

+ 1 - 1
app/src/main/java/com/sduduzog/slimlauncher/utils/HomeWatcher.kt → app/src/main/java/com/simplauncher/utils/HomeWatcher.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 import android.content.BroadcastReceiver
 import android.content.Context

+ 1 - 1
app/src/main/java/com/sduduzog/slimlauncher/utils/IPublisher.kt → app/src/main/java/com/simplauncher/utils/IPublisher.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 interface IPublisher{
     fun attachSubscriber(s: ISubscriber)

+ 3 - 0
app/src/main/java/com/simplauncher/utils/ISubscriber.kt

@@ -0,0 +1,3 @@
+package com.simplauncher.utils
+
+interface ISubscriber

+ 7 - 0
app/src/main/java/com/simplauncher/utils/OnAppClickedListener.kt

@@ -0,0 +1,7 @@
+package com.simplauncher.utils
+
+import com.simplauncher.data.model.App
+
+interface OnAppClickedListener{
+    fun onAppClicked(app: App)
+}

+ 1 - 1
app/src/main/java/com/sduduzog/slimlauncher/utils/OnItemActionListener.kt → app/src/main/java/com/simplauncher/utils/OnItemActionListener.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 interface OnItemActionListener {
     fun onViewMoved(oldPosition: Int, newPosition: Int): Boolean

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/utils/OnLaunchAppListener.kt → app/src/main/java/com/simplauncher/utils/OnLaunchAppListener.kt

@@ -1,7 +1,7 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 import android.view.View
-import com.sduduzog.slimlauncher.models.HomeApp
+import com.simplauncher.models.HomeApp
 
 interface OnLaunchAppListener{
     fun onLaunch(app: HomeApp, view: View)

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/utils/OnShitDoneToAppsListener.kt → app/src/main/java/com/simplauncher/utils/OnShitDoneToAppsListener.kt

@@ -1,7 +1,7 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 import android.view.View
-import com.sduduzog.slimlauncher.models.HomeApp
+import com.simplauncher.models.HomeApp
 
 interface OnShitDoneToAppsListener {
     fun onAppsUpdated(list: List<HomeApp>)

+ 2 - 2
app/src/main/java/com/sduduzog/slimlauncher/utils/SystemUiManager.kt → app/src/main/java/com/simplauncher/utils/SystemUiManager.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 import android.annotation.TargetApi
 import android.app.Activity
@@ -14,7 +14,7 @@ import android.view.WindowInsetsController
 import android.view.WindowManager
 import androidx.annotation.RequiresApi
 import androidx.appcompat.app.AppCompatActivity
-import com.sduduzog.slimlauncher.R
+import com.simplauncher.R
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/utils/Utils.kt → app/src/main/java/com/simplauncher/utils/Utils.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 import android.app.Activity
 import android.content.Context
@@ -14,8 +14,8 @@ import android.text.style.TextAppearanceSpan
 import android.util.DisplayMetrics
 import android.view.WindowInsets
 import androidx.annotation.StringRes
-import com.jkuester.unlauncher.datastore.AlignmentFormat
-import com.sduduzog.slimlauncher.R
+import com.simplauncher.datastore.AlignmentFormat
+import com.simplauncher.R
 
 
 private fun isAppDefaultLauncher(context: Context?): Boolean {

+ 3 - 3
app/src/main/java/com/sduduzog/slimlauncher/utils/WallpaperManager.kt → app/src/main/java/com/simplauncher/utils/WallpaperManager.kt

@@ -1,4 +1,4 @@
-package com.sduduzog.slimlauncher.utils
+package com.simplauncher.utils
 
 import android.app.WallpaperManager
 import android.content.res.Resources
@@ -8,8 +8,8 @@ import androidx.annotation.ColorInt
 import androidx.annotation.StyleRes
 import androidx.annotation.WorkerThread
 import androidx.lifecycle.lifecycleScope
-import com.sduduzog.slimlauncher.MainActivity
-import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
+import com.simplauncher.MainActivity
+import com.simplauncher.datasource.UnlauncherDataSource
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import java.io.IOException

+ 20 - 0
app/src/main/proto/app_drawer_folders.proto

@@ -0,0 +1,20 @@
+syntax = "proto3";
+
+option java_package = "com.simplauncher.datastore";
+option java_multiple_files = true;
+
+message FolderApp {
+  string package_name = 1;
+  string class_name = 2;
+  int64 user_serial = 3;
+}
+
+message AppDrawerFolder {
+  string id = 1;
+  string name = 2;
+  repeated FolderApp apps = 3;
+}
+
+message AppDrawerFolders {
+  repeated AppDrawerFolder folders = 1;
+}

+ 1 - 1
app/src/main/proto/core_preferences.proto

@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-option java_package = "com.jkuester.unlauncher.datastore";
+option java_package = "com.simplauncher.datastore";
 option java_multiple_files = true;
 
 message CorePreferences {

+ 1 - 1
app/src/main/proto/quick_button_preferences.proto

@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-option java_package = "com.jkuester.unlauncher.datastore";
+option java_package = "com.simplauncher.datastore";
 option java_multiple_files = true;
 
 message QuickButtonPreferences {

+ 1 - 1
app/src/main/proto/unlauncher_apps.proto

@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-option java_package = "com.jkuester.unlauncher.datastore";
+option java_package = "com.simplauncher.datastore";
 option java_multiple_files = true;
 
 message UnlauncherApp {

+ 9 - 0
app/src/main/res/drawable/ic_folder.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="?attr/colorAccent"
+        android:pathData="M10,4H4C2.9,4 2,4.9 2,6v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
+</vector>

+ 9 - 0
app/src/main/res/drawable/ic_folder_arrow.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="?attr/colorAccent"
+        android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
+</vector>

+ 35 - 0
app/src/main/res/layout/app_drawer_folder_item.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:padding="@dimen/padding">
+
+    <ImageView
+        android:id="@+id/folder_item_arrow_left"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="8dp"
+        android:src="@drawable/ic_folder_arrow"
+        android:visibility="gone"
+        android:contentDescription="@null" />
+
+    <TextView
+        android:id="@+id/folder_item_name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textAppearance="@style/TextAppearance.AppCompat"
+        android:textSize="@dimen/font_size_apps_list_item" />
+
+    <ImageView
+        android:id="@+id/folder_item_arrow_right"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:src="@drawable/ic_folder_arrow"
+        android:visibility="gone"
+        android:contentDescription="@null" />
+
+</LinearLayout>

+ 11 - 0
app/src/main/res/layout/customize_app_drawer_fragment.xml

@@ -78,6 +78,17 @@
                 android:textColor="?switchTextColor"
                 app:layout_constraintTop_toBottomOf="@id/customize_app_drawer_fragment_search_options" />
 
+            <TextView
+                android:id="@+id/customize_app_drawer_fragment_folders"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/margin_list_items"
+                android:text="@string/customize_app_drawer_fragment_folders"
+                android:textAppearance="@style/TextAppearance.AppCompat"
+                android:textSize="@dimen/font_size_customize_options"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/customize_app_drawer_fragment_show_headings_switch" />
+
         </androidx.constraintlayout.widget.ConstraintLayout>
     </ScrollView>
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 8 - 0
app/src/main/res/layout/customize_folder_detail_app_item.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/folder_detail_app_checkbox"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="@dimen/padding"
+    android:textAppearance="@style/TextAppearance.AppCompat"
+    android:textSize="@dimen/font_size_visible_app_list_item" />

+ 59 - 0
app/src/main/res/layout/customize_folder_detail_fragment.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/customize_folder_detail_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginStart="@dimen/margin_sides"
+    android:layout_marginEnd="@dimen/margin_sides"
+    android:layout_marginTop="@dimen/margin_top_small"
+    tools:context=".ui.options.CustomizeFolderDetailFragment">
+
+    <ImageView
+        android:id="@+id/customize_folder_detail_back"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="@dimen/padding"
+        android:paddingStart="0dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:srcCompat="@drawable/ic_back"
+        android:contentDescription="@string/content_description_back"
+        tools:ignore="RtlSymmetry" />
+
+    <TextView
+        android:id="@+id/customize_folder_detail_title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:textColor="?headerTextColor"
+        android:textAppearance="@style/TextAppearance.AppCompat"
+        android:textSize="@dimen/font_size_customize_title"
+        app:layout_constraintStart_toEndOf="@+id/customize_folder_detail_back"
+        app:layout_constraintEnd_toStartOf="@+id/customize_folder_detail_menu"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="My Folder" />
+
+    <ImageView
+        android:id="@+id/customize_folder_detail_menu"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="@dimen/padding"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:srcCompat="@drawable/ic_more_vert"
+        tools:ignore="ContentDescription" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/customize_folder_detail_list"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginTop="@dimen/margin_top"
+        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/customize_folder_detail_title"
+        tools:listitem="@layout/customize_folder_detail_app_item" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 61 - 0
app/src/main/res/layout/customize_folders_fragment.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/customize_folders_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginStart="@dimen/margin_sides"
+    android:layout_marginEnd="@dimen/margin_sides"
+    android:layout_marginTop="@dimen/margin_top_small"
+    tools:context=".ui.options.CustomizeFoldersFragment">
+
+    <ImageView
+        android:id="@+id/customize_folders_fragment_back"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="@dimen/padding"
+        android:paddingStart="0dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:srcCompat="@drawable/ic_back"
+        android:contentDescription="@string/content_description_back"
+        tools:ignore="RtlSymmetry" />
+
+    <TextView
+        android:id="@+id/customize_folders_fragment_title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:textColor="?headerTextColor"
+        android:text="@string/customize_folders_title"
+        android:textAppearance="@style/TextAppearance.AppCompat"
+        android:textSize="@dimen/font_size_customize_title"
+        app:layout_constraintStart_toEndOf="@+id/customize_folders_fragment_back"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/customize_folders_fragment_create"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/margin_list_items"
+        android:layout_marginStart="@dimen/margin_sides_small"
+        android:text="@string/create_folder"
+        android:textAppearance="@style/TextAppearance.AppCompat"
+        android:textSize="@dimen/font_size_customize_options"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/customize_folders_fragment_title" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/customize_folders_fragment_list"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginTop="@dimen/margin_top"
+        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/customize_folders_fragment_create"
+        tools:listitem="@layout/customize_folders_list_item" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 24 - 0
app/src/main/res/layout/customize_folders_list_item.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:padding="@dimen/padding">
+
+    <TextView
+        android:id="@+id/folder_name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textAppearance="@style/TextAppearance.AppCompat"
+        android:textSize="@dimen/font_size_customize_options" />
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_back"
+        android:rotation="180"
+        android:contentDescription="@null" />
+
+</LinearLayout>

+ 2 - 2
app/src/main/res/layout/home_fragment_content.xml

@@ -45,7 +45,7 @@
         app:srcCompat="@drawable/ic_cog"
         tools:ignore="ContentDescription" />
 
-    <com.sduduzog.slimlauncher.ui.main.BinaryClockView
+    <com.simplauncher.ui.main.BinaryClockView
         android:id="@+id/home_fragment_bin_time"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -53,7 +53,7 @@
         custom:border="2"
         custom:distance="10" />
 
-    <com.sduduzog.slimlauncher.ui.main.AnalogClockView
+    <com.simplauncher.ui.main.AnalogClockView
         android:id="@+id/home_fragment_analog_time"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"

+ 5 - 1
app/src/main/res/layout/imageview_border.xml

@@ -1,5 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="wrap_content">
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    tools:viewBindingIgnore="true">
     <stroke
         android:width="3dp"
         android:color="?attr/colorAccent" />

+ 4 - 0
app/src/main/res/menu/app_long_press_menu.xml

@@ -16,6 +16,10 @@
         android:id="@+id/rename"
         android:title="@string/rename_app"
         android:icon="@drawable/ic_rename_app" />
+    <item
+        android:id="@+id/folders"
+        android:title="@string/folder_menu_item"
+        android:icon="@drawable/ic_folder" />
     <item
         android:id="@+id/uninstall"
         android:title="@string/uninstall"

+ 12 - 0
app/src/main/res/menu/folder_detail_menu.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/folder_menu_rename"
+        android:title="@string/rename_folder"
+        android:icon="@drawable/ic_rename_app" />
+    <item
+        android:id="@+id/folder_menu_delete"
+        android:title="@string/delete_folder"
+        android:icon="@drawable/ic_uninstall_app" />
+</menu>

+ 29 - 8
app/src/main/res/navigation/nav_graph.xml

@@ -7,7 +7,7 @@
 
     <fragment
         android:id="@+id/homeFragment"
-        android:name="com.sduduzog.slimlauncher.ui.main.HomeFragment"
+        android:name="com.simplauncher.ui.main.HomeFragment"
         android:label="home_fragment"
         tools:layout="@layout/home_fragment_default">
         <action
@@ -16,7 +16,7 @@
     </fragment>
     <fragment
         android:id="@+id/optionsFragment"
-        android:name="com.sduduzog.slimlauncher.ui.options.OptionsFragment"
+        android:name="com.simplauncher.ui.options.OptionsFragment"
         android:label="options_fragment"
         tools:layout="@layout/options_fragment">
         <action
@@ -31,7 +31,7 @@
     </fragment>
     <fragment
         android:id="@+id/customiseAppsFragment"
-        android:name="com.sduduzog.slimlauncher.ui.options.CustomiseAppsFragment"
+        android:name="com.simplauncher.ui.options.CustomiseAppsFragment"
         android:label="customise_apps_fragment"
         tools:layout="@layout/customise_apps_fragment">
         <action
@@ -40,17 +40,17 @@
     </fragment>
     <fragment
         android:id="@+id/addAppFragment"
-        android:name="com.sduduzog.slimlauncher.ui.options.AddAppFragment"
+        android:name="com.simplauncher.ui.options.AddAppFragment"
         android:label="add_app_fragment"
         tools:layout="@layout/add_app_fragment" />
     <fragment
         android:id="@+id/customiseQuickButtonsFragment"
-        android:name="com.sduduzog.slimlauncher.ui.options.CustomizeQuickButtonsFragment"
+        android:name="com.simplauncher.ui.options.CustomizeQuickButtonsFragment"
         android:label="customise_quick_buttons_fragment"
         tools:layout="@layout/customize_quick_buttons_fragment" />
     <fragment
         android:id="@+id/customiseAppDrawerFragment"
-        android:name="com.sduduzog.slimlauncher.ui.options.CustomizeAppDrawerFragment"
+        android:name="com.simplauncher.ui.options.CustomizeAppDrawerFragment"
         android:label="customise_app_drawer_fragment"
         tools:layout="@layout/customize_app_drawer_fragment">
         <action
@@ -59,16 +59,37 @@
         <action
             android:id="@+id/action_customiseAppDrawerFragment_to_customizeSearchFieldFragment"
             app:destination="@id/customizeSearchFieldFragment" />
+        <action
+            android:id="@+id/action_customiseAppDrawerFragment_to_customizeFoldersFragment"
+            app:destination="@id/customizeFoldersFragment" />
     </fragment>
     <fragment
         android:id="@+id/customiseAppDrawerAppListFragment"
-        android:name="com.sduduzog.slimlauncher.ui.options.CustomizeAppDrawerAppListFragment"
+        android:name="com.simplauncher.ui.options.CustomizeAppDrawerAppListFragment"
         android:label="customise_app_drawer_app_list_fragment"
         tools:layout="@layout/customize_app_drawer_app_list_fragment" />
     <fragment
         android:id="@+id/customizeSearchFieldFragment"
-        android:name="com.sduduzog.slimlauncher.ui.options.CustomizeSearchFieldFragment"
+        android:name="com.simplauncher.ui.options.CustomizeSearchFieldFragment"
         android:label="customize_app_drawer_search_field_fragment"
         tools:layout="@layout/customize_app_drawer_fragment_search_field_options" />
+    <fragment
+        android:id="@+id/customizeFoldersFragment"
+        android:name="com.simplauncher.ui.options.CustomizeFoldersFragment"
+        android:label="customize_folders_fragment"
+        tools:layout="@layout/customize_folders_fragment">
+        <action
+            android:id="@+id/action_customizeFoldersFragment_to_customizeFolderDetailFragment"
+            app:destination="@id/customizeFolderDetailFragment" />
+    </fragment>
+    <fragment
+        android:id="@+id/customizeFolderDetailFragment"
+        android:name="com.simplauncher.ui.options.CustomizeFolderDetailFragment"
+        android:label="customize_folder_detail_fragment"
+        tools:layout="@layout/customize_folder_detail_fragment">
+        <argument
+            android:name="folderId"
+            app:argType="string" />
+    </fragment>
 
 </navigation>

+ 1 - 1
app/src/main/res/values-de/strings.xml

@@ -40,7 +40,7 @@
     <string name="content_description_back">Zurück</string>
     <string name="customise_apps_fragment_add">Hinzufügen</string>
     <string name="customise_apps_fragment_remove_all">Alle entfernen</string>
-    <string name="customize_app_drawer_fragment_auto_theme_wallpaper_subtext_no_default_launcher">Unlauncher muss die standard Start-App sein</string>
+    <string name="customize_app_drawer_fragment_auto_theme_wallpaper_subtext_no_default_launcher">Simplauncher muss die standard Start-App sein</string>
     <string name="customize_app_drawer_fragment_auto_theme_wallpaper_text">Themenhintergrund verwenden</string>
     <string name="customize_app_drawer_fragment_open_keyboard">Tastatur anzeigen</string>
     <string name="customize_app_drawer_fragment_open_keyboard_subtitle">Tastatur anzeigen, wenn der App-Drawer angezeigt wird</string>

+ 8 - 1
app/src/main/res/values/strings.xml

@@ -1,6 +1,6 @@
 <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
 
-    <string name="app_name" translatable="false">Unlauncher</string>
+    <string name="app_name" translatable="false">Simplauncher</string>
     <string name="main_placeholder_clock" translatable="false">00:00</string>
     <string name="main_placeholder_date" translatable="false">Sat, Apr 20</string>
     <string name="prefs_settings" translatable="false">settings</string>
@@ -94,4 +94,11 @@
     <string name="rename_app">Rename App</string>
     <string name="shown">shown</string>
     <string name="uninstall">Uninstall</string>
+    <string name="create_folder">Create Folder</string>
+    <string name="customize_app_drawer_fragment_folders">Folders</string>
+    <string name="customize_folders_title">Folders</string>
+    <string name="delete_folder">Delete Folder</string>
+    <string name="folder_menu_item">Folders</string>
+    <string name="manage_app_folders">Manage Folders</string>
+    <string name="rename_folder">Rename Folder</string>
 </resources>