From d44a376ecc11b63849b08c05141889674b1434a7 Mon Sep 17 00:00:00 2001 From: "Adam M. Szalkowski" Date: Sat, 30 Dec 2023 19:07:45 +0100 Subject: [PATCH] feat: search/filter packages/activities --- .../activitylauncher/MainActivity.kt | 27 ++++++++++++++++++- .../activitylauncher/ui/ActionBarSearch.kt | 6 +++++ .../ui/ActivityDetailsFragment.kt | 4 +++ .../ui/ActivityListAdapter.kt | 20 +++++++++++--- .../ui/ActivityListFragment.kt | 6 +++++ .../activitylauncher/ui/PackageListAdapter.kt | 21 ++++++++++++--- .../ui/PackageListFragment.kt | 6 +++++ 7 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/de/szalkowski/activitylauncher/ui/ActionBarSearch.kt diff --git a/app/src/main/java/de/szalkowski/activitylauncher/MainActivity.kt b/app/src/main/java/de/szalkowski/activitylauncher/MainActivity.kt index f1646279..f771ff51 100644 --- a/app/src/main/java/de/szalkowski/activitylauncher/MainActivity.kt +++ b/app/src/main/java/de/szalkowski/activitylauncher/MainActivity.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.SearchView import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.navigateUp @@ -12,11 +13,12 @@ import androidx.navigation.ui.setupActionBarWithNavController import dagger.hilt.android.AndroidEntryPoint import de.szalkowski.activitylauncher.databinding.ActivityMainBinding import de.szalkowski.activitylauncher.services.SettingsService +import de.szalkowski.activitylauncher.ui.ActionBarSearch import de.szalkowski.activitylauncher.ui.DisclaimerDialogFragment import javax.inject.Inject @AndroidEntryPoint -class MainActivity : AppCompatActivity() { +class MainActivity() : AppCompatActivity(), ActionBarSearch { private lateinit var appBarConfiguration: AppBarConfiguration private lateinit var binding: ActivityMainBinding @@ -41,9 +43,32 @@ class MainActivity : AppCompatActivity() { setupActionBarWithNavController(navController, appBarConfiguration) } + + override var onActionBarSearchListener: ((String) -> Unit)? = null + private var actionBarSearchView: SearchView? = null + override var actionBarSearchText: String + get() = actionBarSearchView?.query?.toString().orEmpty() + set(value) { actionBarSearchView?.setQuery(value, false) } + override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu; this adds items to the action bar if it is present. menuInflater.inflate(R.menu.menu_main, menu) + + val searchView = menu.findItem(R.id.search).actionView as SearchView + actionBarSearchView = searchView + searchView.queryHint = getText(R.string.filter_hint) + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + onActionBarSearchListener?.invoke(query.orEmpty()) + return true + } + + override fun onQueryTextChange(query: String?): Boolean { + onActionBarSearchListener?.invoke(query.orEmpty()) + return true + } + }) + return true } diff --git a/app/src/main/java/de/szalkowski/activitylauncher/ui/ActionBarSearch.kt b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActionBarSearch.kt new file mode 100644 index 00000000..a7c51dfa --- /dev/null +++ b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActionBarSearch.kt @@ -0,0 +1,6 @@ +package de.szalkowski.activitylauncher.ui + +interface ActionBarSearch { + var actionBarSearchText: String + var onActionBarSearchListener: ((String) -> Unit)? +} \ No newline at end of file diff --git a/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityDetailsFragment.kt b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityDetailsFragment.kt index 98ad6055..f182d9da 100644 --- a/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityDetailsFragment.kt +++ b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityDetailsFragment.kt @@ -51,6 +51,10 @@ class ActivityDetailsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + val actionBar = activity as? ActionBarSearch + // FIXME just hide the search menu item, instead + actionBar?.actionBarSearchText = "" + binding.tiName.setText(activityInfo.name) binding.tiPackage.setText(activityInfo.componentName.packageName) binding.tiClass.setText(activityInfo.componentName.shortClassName) diff --git a/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListAdapter.kt b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListAdapter.kt index 81636495..59a87e2b 100644 --- a/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListAdapter.kt +++ b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListAdapter.kt @@ -22,7 +22,8 @@ class ActivityListAdapter @AssistedInject constructor( fun create(packageName: String): ActivityListAdapter } - private val activities = activityListService.getActivities(packageName) + private val allActivities = activityListService.getActivities(packageName) + private var filteredActivities = allActivities var onItemClick: ((MyActivityInfo) -> Unit)? = null inner class ViewHolder(viewItem: View) : RecyclerView.ViewHolder(viewItem) { @@ -35,6 +36,19 @@ class ActivityListAdapter @AssistedInject constructor( } } + var filter: String = "" + set(value) { + field = value + filteredActivities = allActivities.filter { a -> + listOf(a.name, a.componentName.className).any { + it.contains( + field, ignoreCase = true + ) + } + } + notifyDataSetChanged() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) val view = inflater.inflate(R.layout.list_item_package_list, parent, false) @@ -42,7 +56,7 @@ class ActivityListAdapter @AssistedInject constructor( } override fun getItemCount(): Int { - return activities.size + return filteredActivities.size } override fun onBindViewHolder(holder: ViewHolder, position: Int) { @@ -51,7 +65,7 @@ class ActivityListAdapter @AssistedInject constructor( val tvPackage = view.findViewById(R.id.tvPackage) val ivIcon = view.findViewById(R.id.ivIcon) - val item = activities[position] + val item = filteredActivities[position] holder.item = item tvName.text = if (item.isPrivate) { "(${item.name})" diff --git a/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListFragment.kt b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListFragment.kt index 1d5ae0a3..a88ec2bd 100644 --- a/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListFragment.kt +++ b/app/src/main/java/de/szalkowski/activitylauncher/ui/ActivityListFragment.kt @@ -41,6 +41,12 @@ class ActivityListFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + val actionBar = activity as? ActionBarSearch + activityListAdapter.filter = actionBar?.actionBarSearchText.orEmpty() + actionBar?.onActionBarSearchListener = { search -> + activityListAdapter.filter = search + } + activityListAdapter.onItemClick = { val action = ActivityListFragmentDirections.actionSelectActivity(it.componentName) findNavController().navigate(action) diff --git a/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListAdapter.kt b/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListAdapter.kt index 197de74a..9d86ab35 100644 --- a/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListAdapter.kt +++ b/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListAdapter.kt @@ -14,7 +14,9 @@ import javax.inject.Inject class PackageListAdapter @Inject constructor(packageListService: PackageListService) : RecyclerView.Adapter() { - private val packages = packageListService.packages + private val allPackages = packageListService.packages + private var filteredPackages = allPackages + var onItemClick: ((MyPackageInfo) -> Unit)? = null inner class ViewHolder(viewItem: View) : RecyclerView.ViewHolder(viewItem) { @@ -27,6 +29,19 @@ class PackageListAdapter @Inject constructor(packageListService: PackageListServ } } + var filter: String = "" + set(value) { + field = value + filteredPackages = allPackages.filter { p -> + listOf(p.name, p.packageName).any { + it.contains( + field, ignoreCase = true + ) + } + } + notifyDataSetChanged() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) val view = inflater.inflate(R.layout.list_item_package_list, parent, false) @@ -34,7 +49,7 @@ class PackageListAdapter @Inject constructor(packageListService: PackageListServ } override fun getItemCount(): Int { - return packages.size + return filteredPackages.size } override fun onBindViewHolder(holder: ViewHolder, position: Int) { @@ -43,7 +58,7 @@ class PackageListAdapter @Inject constructor(packageListService: PackageListServ val tvPackage = view.findViewById(R.id.tvPackage) val ivIcon = view.findViewById(R.id.ivIcon) - val item = packages[position] + val item = filteredPackages[position] holder.item = item tvName.text = item.name tvPackage.text = item.packageName diff --git a/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListFragment.kt b/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListFragment.kt index 3ea2e242..29104457 100644 --- a/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListFragment.kt +++ b/app/src/main/java/de/szalkowski/activitylauncher/ui/PackageListFragment.kt @@ -31,6 +31,12 @@ class PackageListFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + val actionBar = activity as? ActionBarSearch + packageListAdapter.filter = actionBar?.actionBarSearchText.orEmpty() + actionBar?.onActionBarSearchListener = { search -> + packageListAdapter.filter = search + } + packageListAdapter.onItemClick = { val action = PackageListFragmentDirections.actionSelectPackage(it.packageName) findNavController().navigate(action)