From d76f9ffd784bfe061b36fc3321b01b7767483715 Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Fri, 26 Jul 2024 15:28:02 +0530 Subject: [PATCH] Use list adapter in app drawer --- .../launcher/adapters/LaunchersAdapter.kt | 74 ++++++------------- .../launcher/fragments/AllAppsFragment.kt | 56 ++++++++------ 2 files changed, 55 insertions(+), 75 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/launcher/adapters/LaunchersAdapter.kt b/app/src/main/kotlin/org/fossify/launcher/adapters/LaunchersAdapter.kt index 74222581..22094035 100644 --- a/app/src/main/kotlin/org/fossify/launcher/adapters/LaunchersAdapter.kt +++ b/app/src/main/kotlin/org/fossify/launcher/adapters/LaunchersAdapter.kt @@ -1,9 +1,12 @@ package org.fossify.launcher.adapters +import android.annotation.SuppressLint import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy @@ -21,78 +24,41 @@ import org.fossify.launcher.databinding.ItemLauncherLabelBinding import org.fossify.launcher.extensions.config import org.fossify.launcher.interfaces.AllAppsListener import org.fossify.launcher.models.AppLauncher -import org.fossify.launcher.models.HomeScreenGridItem class LaunchersAdapter( val activity: SimpleActivity, - launchers: ArrayList, val allAppsListener: AllAppsListener, val itemClick: (Any) -> Unit -) : RecyclerView.Adapter(), RecyclerViewFastScroller.OnPopupTextUpdate { +) : ListAdapter(AppLauncherDiffCallback()), RecyclerViewFastScroller.OnPopupTextUpdate { private var textColor = activity.getProperTextColor() private var iconPadding = 0 - private var wereFreshIconsLoaded = false - private var filterQuery: String? = null - private var filteredLaunchers: List = launchers - - var launchers: ArrayList = launchers - set(value) { - field = value - updateFilter() - } init { + setHasStableIds(true) calculateIconWidth() } + override fun getItemId(position: Int): Long { + return getItem(position).getLauncherIdentifier().hashCode().toLong() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemLauncherLabelBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ViewHolder(binding.root) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.bindView(filteredLaunchers[position]) + holder.bindView(getItem(position)) } - override fun getItemCount() = filteredLaunchers.size - private fun calculateIconWidth() { val currentColumnCount = activity.config.drawerColumnCount - val iconWidth = activity.realScreenSize.x / currentColumnCount iconPadding = (iconWidth * 0.1f).toInt() } - fun hideIcon(item: HomeScreenGridItem) { - val itemToRemove = launchers.firstOrNull { it.getLauncherIdentifier() == item.getItemIdentifier() } - if (itemToRemove != null) { - val position = launchers.indexOfFirst { it.getLauncherIdentifier() == item.getItemIdentifier() } - val filteredPosition = filteredLaunchers.indexOfFirst { it.getLauncherIdentifier() == item.getItemIdentifier() } - launchers.removeAt(position) - updateFilter() - notifyItemRemoved(filteredPosition) - } - } - - fun updateItems(newItems: ArrayList) { - val oldSum = launchers.sumOf { it.getHashToCompare() } - val newSum = newItems.sumOf { it.getHashToCompare() } - if (oldSum != newSum || !wereFreshIconsLoaded) { - launchers = newItems - notifyDataSetChanged() - wereFreshIconsLoaded = true - } - } - - fun updateSearchQuery(newQuery: String?) { - if (filterQuery != newQuery) { - filterQuery = newQuery - updateFilter() - notifyDataSetChanged() - } - } - + @SuppressLint("NotifyDataSetChanged") fun updateTextColor(newTextColor: Int) { if (newTextColor != textColor) { textColor = newTextColor @@ -100,10 +66,6 @@ class LaunchersAdapter( } } - private fun updateFilter() { - filteredLaunchers = launchers.filter { filterQuery == null || it.title.contains(filterQuery!!, ignoreCase = true) } - } - inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { fun bindView(launcher: AppLauncher): View { val binding = ItemLauncherLabelBinding.bind(itemView) @@ -112,7 +74,6 @@ class LaunchersAdapter( binding.launcherLabel.setTextColor(textColor) binding.launcherIcon.setPadding(iconPadding, iconPadding, iconPadding, 0) - // Once all images are loaded and crossfades are done, directly set drawables if (launcher.drawable != null && binding.launcherIcon.tag == true) { binding.launcherIcon.setImageDrawable(launcher.drawable) } else { @@ -127,14 +88,13 @@ class LaunchersAdapter( .into(object : DrawableImageViewTarget(binding.launcherIcon) { override fun onResourceReady(resource: Drawable, transition: Transition?) { super.onResourceReady(resource, transition) - // Set tag to true to mark that crossfade was already done on this view view.tag = true } }) } setOnClickListener { itemClick(launcher) } - setOnLongClickListener { view -> + setOnLongClickListener { val location = IntArray(2) getLocationOnScreen(location) allAppsListener.onAppLauncherLongPressed((location[0] + width / 2).toFloat(), location[1].toFloat(), launcher) @@ -146,5 +106,13 @@ class LaunchersAdapter( } } - override fun onChange(position: Int) = filteredLaunchers.getOrNull(position)?.getBubbleText() ?: "" + override fun onChange(position: Int) = currentList.getOrNull(position)?.getBubbleText() ?: "" +} + +private class AppLauncherDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: AppLauncher, newItem: AppLauncher): Boolean { + return oldItem.getLauncherIdentifier().hashCode().toLong() == newItem.getLauncherIdentifier().hashCode().toLong() + } + + override fun areContentsTheSame(oldItem: AppLauncher, newItem: AppLauncher) = oldItem == newItem } diff --git a/app/src/main/kotlin/org/fossify/launcher/fragments/AllAppsFragment.kt b/app/src/main/kotlin/org/fossify/launcher/fragments/AllAppsFragment.kt index bdde9cd9..2ffb4d28 100644 --- a/app/src/main/kotlin/org/fossify/launcher/fragments/AllAppsFragment.kt +++ b/app/src/main/kotlin/org/fossify/launcher/fragments/AllAppsFragment.kt @@ -26,6 +26,8 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment var ignoreTouches = false var hasTopPadding = false + private var launchers = emptyList() + @SuppressLint("ClickableViewAccessibility") override fun setupFragment(activity: MainActivity) { this.activity = activity @@ -61,7 +63,6 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment val layoutManager = binding.allAppsGrid.layoutManager as MyGridLayoutManager layoutManager.spanCount = context.config.drawerColumnCount - val launchers = (binding.allAppsGrid.adapter as LaunchersAdapter).launchers setupAdapter(launchers) } @@ -95,26 +96,27 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment return shouldIntercept } - fun gotLaunchers(appLaunchers: ArrayList) { - val sorted = appLaunchers.sortedWith( - compareBy({ - it.title.normalizeString().lowercase() - }, { - it.packageName - }) - ).toMutableList() as ArrayList + fun gotLaunchers(appLaunchers: List) { + launchers = appLaunchers.sortedWith( + compareBy( + { it.title.normalizeString().lowercase() }, + { it.packageName } + ) + ) - setupAdapter(sorted) + setupAdapter(launchers) } - private fun setupAdapter(launchers: ArrayList) { + private fun getAdapter() = binding.allAppsGrid.adapter as? LaunchersAdapter + + private fun setupAdapter(launchers: List) { activity?.runOnUiThread { val layoutManager = binding.allAppsGrid.layoutManager as MyGridLayoutManager layoutManager.spanCount = context.config.drawerColumnCount - val currAdapter = binding.allAppsGrid.adapter - if (currAdapter == null) { - LaunchersAdapter(activity!!, launchers, this) { + var adapter = getAdapter() + if (adapter == null) { + adapter = LaunchersAdapter(activity!!, this) { activity?.launchApp((it as AppLauncher).packageName, it.activityName) if (activity?.config?.closeAppDrawer == true) { activity?.closeAppDrawer(delayed = true) @@ -124,14 +126,22 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment }.apply { binding.allAppsGrid.adapter = this } - } else { - (currAdapter as LaunchersAdapter).updateItems(launchers) } + + adapter.submitList(launchers.toMutableList()) } } fun hideIcon(item: HomeScreenGridItem) { - (binding.allAppsGrid.adapter as? LaunchersAdapter)?.hideIcon(item) + val itemToRemove = launchers.firstOrNull { it.getLauncherIdentifier() == item.getItemIdentifier() } + if (itemToRemove != null) { + val position = launchers.indexOfFirst { it.getLauncherIdentifier() == item.getItemIdentifier() } + launchers = launchers.toMutableList().apply { + removeAt(position) + } + + getAdapter()?.submitList(launchers) + } } fun setupViews(addTopPadding: Boolean = hasTopPadding) { @@ -172,21 +182,23 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment val topPadding = if (addTopPadding) activity!!.statusBarHeight else 0 setPadding(0, topPadding, 0, 0) background = ColorDrawable(context.getProperBackgroundColor()) - (binding.allAppsGrid.adapter as? LaunchersAdapter)?.updateTextColor(context.getProperTextColor()) + getAdapter()?.updateTextColor(context.getProperTextColor()) binding.searchBar.beVisibleIf(context.config.showSearchBar) binding.searchBar.getToolbar().beGone() binding.searchBar.updateColors() binding.searchBar.setupMenu() - binding.searchBar.onSearchTextChangedListener = { - (binding.allAppsGrid.adapter as? LaunchersAdapter)?.updateSearchQuery(it) - showNoResultsPlaceholderIfNeeded() + binding.searchBar.onSearchTextChangedListener = { query -> + val filtered = launchers.filter { query.isEmpty() || it.title.contains(query, ignoreCase = true) } + getAdapter()?.submitList(filtered) { + showNoResultsPlaceholderIfNeeded() + } } } private fun showNoResultsPlaceholderIfNeeded() { - val itemCount = binding.allAppsGrid.adapter?.itemCount + val itemCount = getAdapter()?.itemCount binding.noResultsPlaceholder.beVisibleIf(itemCount != null && itemCount == 0) }