From 40776412db3346fa6816cdd773b47a45057784dc Mon Sep 17 00:00:00 2001 From: Nullptr Date: Wed, 10 Aug 2022 17:17:43 +0800 Subject: [PATCH] [skip ci] Better recyclerview --- .../java/icu/nullptr/hidemyapplist/MyApp.kt | 2 +- .../hidemyapplist/service/ConfigManager.kt | 4 + .../hidemyapplist/ui/adapter/AppAdapter.kt | 64 -------------- .../ui/adapter/AppManageAdapter.kt | 26 ++++++ .../ui/adapter/AppScopeAdapter.kt | 43 ++++++++++ .../ui/adapter/AppSelectAdapter.kt | 54 ++++++++++++ .../ui/fragment/AppManageFragment.kt | 23 ++++- .../ui/fragment/AppSelectFragment.kt | 17 +--- .../hidemyapplist/ui/fragment/HomeFragment.kt | 6 +- .../ui/fragment/ScopeFragment.kt | 30 +++++++ .../ui/fragment/TemplateSettingsFragment.kt | 21 ++--- .../hidemyapplist/ui/view/AppItemView.kt | 3 +- .../ui/viewmodel/AppSelectViewModel.kt | 86 ++++--------------- .../ui/viewmodel/TemplateSettingsViewModel.kt | 25 +++++- .../hidemyapplist/util/PackageHelper.kt | 80 +++++++++++++---- app/src/main/res/layout/app_item_view.xml | 5 +- .../main/res/layout/fragment_app_manage.xml | 6 -- .../main/res/layout/fragment_app_select.xml | 10 +-- app/src/main/res/layout/fragment_settings.xml | 7 +- app/src/main/res/navigation/nav_graph.xml | 5 +- app/src/main/res/values/dimens.xml | 4 +- build.gradle.kts | 2 +- gradle.properties | 2 +- 23 files changed, 310 insertions(+), 215 deletions(-) delete mode 100644 app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppAdapter.kt create mode 100644 app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt create mode 100644 app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppScopeAdapter.kt create mode 100644 app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppSelectAdapter.kt create mode 100644 app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/ScopeFragment.kt delete mode 100644 app/src/main/res/layout/fragment_app_manage.xml diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt b/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt index 2294f3b7..a8d2b7f1 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt @@ -20,7 +20,7 @@ class MyApp : Application() { val globalScope = CoroutineScope(Dispatchers.Default) val appIconLoader by lazy { val iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size) - AppIconLoader(iconSize, true, this) + AppIconLoader(iconSize, false, this) } @SuppressLint("SdCardPath") diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt index 46e5e5d9..e7bca4d7 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt @@ -87,4 +87,8 @@ object ConfigManager { } saveConfig() } + + fun isUsingHide(packageName: String): Boolean { + return config.scope.containsKey(packageName) + } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppAdapter.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppAdapter.kt deleted file mode 100644 index ebbaa368..00000000 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppAdapter.kt +++ /dev/null @@ -1,64 +0,0 @@ -package icu.nullptr.hidemyapplist.ui.adapter - -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import icu.nullptr.hidemyapplist.service.PrefManager -import icu.nullptr.hidemyapplist.ui.view.AppItemView -import icu.nullptr.hidemyapplist.ui.viewmodel.AppSelectViewModel -import icu.nullptr.hidemyapplist.util.PackageHelper - -class AppAdapter( - private val viewModel: AppSelectViewModel -) : RecyclerView.Adapter() { - - var onClickListener: ((String) -> Unit)? = null - - inner class ViewHolder(view: AppItemView) : RecyclerView.ViewHolder(view) { - - init { - view.setOnClickListener { - with(viewModel) { - val packageName = list[absoluteAdapterPosition] - onClickListener?.invoke(packageName) - if (isMultiSelect) { - packageName.isChecked = !packageName.isChecked - view.isChecked = packageName.isChecked - } - } - } - } - - fun bind(packageName: String) { - with(viewModel) { - (itemView as AppItemView).let { - it.load(packageName) - if (isMultiSelect) it.isChecked = packageName.isChecked - else it.showEnabled = packageName.isChecked - if (!PrefManager.filter_showSystem && PackageHelper.isSystem(packageName)) { - it.visibility = ViewGroup.GONE - it.layoutParams.height = 0 - } else { - it.visibility = ViewGroup.VISIBLE - it.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT - } - } - } - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = AppItemView(parent.context, viewModel.isMultiSelect) - view.showEnabled = !viewModel.isMultiSelect - view.layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) - return ViewHolder(view) - } - - override fun getItemCount() = viewModel.list.size - - override fun getItemId(position: Int) = viewModel.list[position].hashCode().toLong() - - override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(viewModel.list[position]) -} diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt new file mode 100644 index 00000000..18b38307 --- /dev/null +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt @@ -0,0 +1,26 @@ +package icu.nullptr.hidemyapplist.ui.adapter + +import android.view.ViewGroup +import icu.nullptr.hidemyapplist.service.ConfigManager +import icu.nullptr.hidemyapplist.ui.view.AppItemView + +class AppManageAdapter : AppSelectAdapter() { + + inner class ViewHolder(view: AppItemView) : AppSelectAdapter.ViewHolder(view) { + override fun bind(packageName: String) { + (itemView as AppItemView).let { + it.load(packageName) + it.showEnabled = ConfigManager.isUsingHide(packageName) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = AppItemView(parent.context, false) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + return ViewHolder(view) + } +} diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppScopeAdapter.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppScopeAdapter.kt new file mode 100644 index 00000000..e8c8ea1f --- /dev/null +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppScopeAdapter.kt @@ -0,0 +1,43 @@ +package icu.nullptr.hidemyapplist.ui.adapter + +import android.view.ViewGroup +import icu.nullptr.hidemyapplist.service.ConfigManager +import icu.nullptr.hidemyapplist.ui.view.AppItemView + +class AppScopeAdapter( + filterOnlyEnabled: Boolean, + private val checked: MutableSet +) : AppSelectAdapter(if (filterOnlyEnabled) ConfigManager::isUsingHide else null) { + + private inline var String.isChecked + get() = checked.contains(this) + set(value) { + if (value) checked.add(this) else checked.remove(this) + } + + inner class ViewHolder(view: AppItemView) : AppSelectAdapter.ViewHolder(view) { + init { + view.setOnClickListener { + val packageName = filteredList[absoluteAdapterPosition] + packageName.isChecked = !packageName.isChecked + view.isChecked = packageName.isChecked + } + } + + override fun bind(packageName: String) { + (itemView as AppItemView).let { + it.load(packageName) + it.isChecked = packageName.isChecked + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = AppItemView(parent.context, true) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + return ViewHolder(view) + } +} diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppSelectAdapter.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppSelectAdapter.kt new file mode 100644 index 00000000..83a6c640 --- /dev/null +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppSelectAdapter.kt @@ -0,0 +1,54 @@ +package icu.nullptr.hidemyapplist.ui.adapter + +import android.widget.Filter +import android.widget.Filterable +import androidx.recyclerview.widget.RecyclerView +import icu.nullptr.hidemyapplist.service.PrefManager +import icu.nullptr.hidemyapplist.ui.view.AppItemView +import icu.nullptr.hidemyapplist.util.PackageHelper +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking + +abstract class AppSelectAdapter( + private val firstFilter: ((String) -> Boolean)? = null +) : RecyclerView.Adapter(), Filterable { + + abstract class ViewHolder(view: AppItemView) : RecyclerView.ViewHolder(view) { + abstract fun bind(packageName: String) + } + + private inner class AppFilter : Filter() { + override fun performFiltering(constraint: CharSequence): FilterResults { + return runBlocking { + val constraintLowered = constraint.toString().lowercase() + val filteredList = PackageHelper.appList.first().filter { + if (firstFilter?.invoke(it) == false) return@filter false + if (!PrefManager.filter_showSystem && PackageHelper.isSystem(it)) return@filter false + val label = PackageHelper.loadAppLabel(it) + val packageInfo = PackageHelper.loadPackageInfo(it) + label.lowercase().contains(constraintLowered) || packageInfo.packageName.lowercase().contains(constraintLowered) + } + + FilterResults().also { it.values = filteredList } + } + } + + @Suppress("UNCHECKED_CAST", "NotifyDataSetChanged") + override fun publishResults(constraint: CharSequence, results: FilterResults) { + filteredList = results.values as List + notifyDataSetChanged() + } + } + + private val mFilter = AppFilter() + + protected var filteredList: List = listOf() + + override fun getItemCount() = filteredList.size + + override fun getItemId(position: Int) = filteredList[position].hashCode().toLong() + + override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(filteredList[position]) + + override fun getFilter(): Filter = mFilter +} diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt index ae11da59..6495ead2 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt @@ -1,7 +1,24 @@ package icu.nullptr.hidemyapplist.ui.fragment -import androidx.fragment.app.Fragment -import com.tsng.hidemyapplist.R +import android.os.Bundle +import androidx.fragment.app.viewModels +import com.google.android.material.transition.MaterialSharedAxis +import icu.nullptr.hidemyapplist.service.ConfigManager +import icu.nullptr.hidemyapplist.ui.adapter.AppManageAdapter +import icu.nullptr.hidemyapplist.ui.viewmodel.AppSelectViewModel -class AppManageFragment : Fragment(R.layout.fragment_app_manage) { +class AppManageFragment : AppSelectFragment() { + + override val viewModel by viewModels { + AppSelectViewModel.Factory( + firstComparator = Comparator.comparing(ConfigManager::isUsingHide).reversed(), + adapter = AppManageAdapter() + ) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) + } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt index ab888104..0866dafc 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt @@ -5,10 +5,7 @@ import android.view.MenuItem import android.view.View import androidx.activity.addCallback import androidx.fragment.app.Fragment -import androidx.fragment.app.setFragmentResult -import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import by.kirich1409.viewbindingdelegate.viewBinding import com.tsng.hidemyapplist.R @@ -20,20 +17,12 @@ import icu.nullptr.hidemyapplist.ui.viewmodel.AppSelectViewModel import icu.nullptr.hidemyapplist.util.PackageHelper import kotlinx.coroutines.launch -class AppSelectFragment : Fragment(R.layout.fragment_app_select) { +abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) { private val binding by viewBinding() - private val args by navArgs() - private val viewModel by viewModels { - AppSelectViewModel.Factory(args) - } + protected abstract val viewModel: AppSelectViewModel - private fun onBack() { - if (viewModel.isMultiSelect) { - setFragmentResult("app_select", Bundle().apply { - putStringArrayList("checked", ArrayList(viewModel.checked)) - }) - } + protected open fun onBack() { navController.navigateUp() } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/HomeFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/HomeFragment.kt index a8f1978e..00f75d92 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/HomeFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/HomeFragment.kt @@ -5,6 +5,7 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.navigation.fragment.FragmentNavigatorExtras import by.kirich1409.viewbindingdelegate.viewBinding +import com.google.android.gms.ads.AdRequest import com.google.android.material.transition.MaterialElevationScale import com.tsng.hidemyapplist.BuildConfig import com.tsng.hidemyapplist.R @@ -26,14 +27,13 @@ class HomeFragment : Fragment(R.layout.fragment_home) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { setupToolbar(binding.toolbar, getString(R.string.app_name)) - // binding.adBanner.loadAd(AdRequest.Builder().build()) + binding.adBanner.loadAd(AdRequest.Builder().build()) binding.templateManage.setOnClickListener { val extras = FragmentNavigatorExtras(binding.manageCard to "transition_manage") navController.navigate(R.id.nav_template_manage, null, null, extras) } binding.appManage.setOnClickListener { - val extras = FragmentNavigatorExtras(binding.manageCard as View to "transition_manage") - navController.navigate(R.id.nav_app_manage, null, null, extras) + navController.navigate(R.id.nav_app_manage) } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/ScopeFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/ScopeFragment.kt new file mode 100644 index 00000000..dd55b2e9 --- /dev/null +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/ScopeFragment.kt @@ -0,0 +1,30 @@ +package icu.nullptr.hidemyapplist.ui.fragment + +import android.os.Bundle +import androidx.fragment.app.setFragmentResult +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.navArgs +import icu.nullptr.hidemyapplist.ui.adapter.AppScopeAdapter +import icu.nullptr.hidemyapplist.ui.util.navController +import icu.nullptr.hidemyapplist.ui.viewmodel.AppSelectViewModel + +class ScopeFragment : AppSelectFragment() { + + private lateinit var checked: MutableSet + + override val viewModel by viewModels { + val args by navArgs() + checked = args.checked.toMutableSet() + AppSelectViewModel.Factory( + firstComparator = Comparator.comparing { !checked.contains(it) }, + adapter = AppScopeAdapter(args.filterOnlyEnabled, checked) + ) + } + + override fun onBack() { + setFragmentResult("app_select", Bundle().apply { + putStringArrayList("checked", ArrayList(checked)) + }) + navController.navigateUp() + } +} diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/TemplateSettingsFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/TemplateSettingsFragment.kt index ffa84b8a..f1728fe0 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/TemplateSettingsFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/TemplateSettingsFragment.kt @@ -9,6 +9,7 @@ import androidx.appcompat.view.ContextThemeWrapper import androidx.core.widget.addTextChangedListener import androidx.fragment.app.* import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.navArgs import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.transition.MaterialContainerTransform @@ -23,7 +24,10 @@ import kotlinx.coroutines.launch class TemplateSettingsFragment : Fragment(R.layout.fragment_template_settings) { private val binding by viewBinding() - private val viewModel by viewModels() + private val args by navArgs() + private val viewModel by viewModels() { + TemplateSettingsViewModel.Factory(args) + } private fun onBack() { viewModel.name = viewModel.name?.trim() @@ -49,15 +53,6 @@ class TemplateSettingsFragment : Fragment(R.layout.fragment_template_settings) { drawingViewId = R.id.nav_host_fragment scrimColor = Color.TRANSPARENT } - - val args = TemplateSettingsFragmentArgs.fromBundle(requireArguments()) - args.name?.let { - viewModel.name = it - viewModel.originalName = it - viewModel.appliedAppList.value = ConfigManager.getTemplateAppliedAppList(it) - viewModel.targetAppList.value = ConfigManager.getTemplateTargetAppList(it) - } - viewModel.isWhiteList = args.isWhiteList } override fun onGetLayoutInflater(savedInstanceState: Bundle?): LayoutInflater { @@ -87,8 +82,7 @@ class TemplateSettingsFragment : Fragment(R.layout.fragment_template_settings) { viewModel.targetAppList.value = bundle.getStringArrayList("checked")!! clearFragmentResultListener("app_select") } - val selectArgs = AppSelectFragmentArgs( - isMultiSelect = true, + val selectArgs = ScopeFragmentArgs( filterOnlyEnabled = false, checked = viewModel.targetAppList.value.toTypedArray() ) @@ -99,8 +93,7 @@ class TemplateSettingsFragment : Fragment(R.layout.fragment_template_settings) { viewModel.appliedAppList.value = bundle.getStringArrayList("checked")!! clearFragmentResultListener("app_select") } - val selectArgs = AppSelectFragmentArgs( - isMultiSelect = true, + val selectArgs = ScopeFragmentArgs( filterOnlyEnabled = true, checked = viewModel.appliedAppList.value.toTypedArray() ) diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt index db409a13..c458cd17 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt @@ -6,7 +6,6 @@ import android.widget.LinearLayout import by.kirich1409.viewbindingdelegate.CreateMethod import by.kirich1409.viewbindingdelegate.viewBinding import com.tsng.hidemyapplist.databinding.AppItemViewBinding -import icu.nullptr.hidemyapplist.hmaApp import icu.nullptr.hidemyapplist.util.PackageHelper class AppItemView @JvmOverloads constructor( @@ -37,6 +36,6 @@ class AppItemView @JvmOverloads constructor( fun load(packageName: String) { binding.packageName.text = packageName binding.label.text = PackageHelper.loadAppLabel(packageName) - binding.icon.setImageBitmap(hmaApp.appIconLoader.loadIcon(PackageHelper.loadPackageInfo(packageName).applicationInfo)) + binding.icon.setImageBitmap(PackageHelper.loadAppIcon(packageName)) } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/AppSelectViewModel.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/AppSelectViewModel.kt index 031a7c06..b56ee053 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/AppSelectViewModel.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/AppSelectViewModel.kt @@ -3,97 +3,41 @@ package icu.nullptr.hidemyapplist.ui.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import icu.nullptr.hidemyapplist.service.PrefManager -import icu.nullptr.hidemyapplist.ui.adapter.AppAdapter -import icu.nullptr.hidemyapplist.ui.fragment.AppSelectFragmentArgs +import icu.nullptr.hidemyapplist.ui.adapter.AppSelectAdapter import icu.nullptr.hidemyapplist.util.PackageHelper import kotlinx.coroutines.launch -import java.text.Collator -import java.util.* class AppSelectViewModel( - val isMultiSelect: Boolean, - private val filterOnlyEnabled: Boolean, - val checked: MutableSet + private val firstComparator: Comparator, + val adapter: AppSelectAdapter ) : ViewModel() { - class Factory(private val args: AppSelectFragmentArgs) : ViewModelProvider.Factory { + class Factory( + private val firstComparator: Comparator, + private val adapter: AppSelectAdapter + ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(AppSelectViewModel::class.java)) { @Suppress("UNCHECKED_CAST") - return AppSelectViewModel(args.isMultiSelect, args.filterOnlyEnabled, args.checked.toMutableSet()) as T + return AppSelectViewModel(firstComparator, adapter) as T } else throw IllegalArgumentException("Unknown ViewModel class") } } - val adapter = AppAdapter(this) - - var list = listOf() - private set - - private val comparators = Comparators() + var search = "" init { - viewModelScope.launch { - PackageHelper.packageCache.collect { cache -> - val newList = cache.mapTo(mutableListOf()) { it.key } - sortList(newList) - list = newList - adapter.notifyDataSetChanged() - } - } + sortList() } - inline var String.isChecked - get() = checked.contains(this) - set(value) { - if (value) checked.add(this) else checked.remove(this) - } + fun applyFilter() { + adapter.filter.filter(search) + } fun sortList() { viewModelScope.launch { - val newList = list.toMutableList() - sortList(newList) - list = newList - adapter.notifyDataSetChanged() - } - } - - private fun sortList(listToSort: MutableList) { - if (filterOnlyEnabled) listToSort.removeIf { !it.isChecked } - var comparator = when (PrefManager.filter_sortMethod) { - PrefManager.SortMethod.BY_LABEL -> comparators.byLabel - PrefManager.SortMethod.BY_PACKAGE_NAME -> comparators.byPackageName - PrefManager.SortMethod.BY_INSTALL_TIME -> comparators.byInstallTime - PrefManager.SortMethod.BY_UPDATE_TIME -> comparators.byUpdateTime - } - if (PrefManager.filter_reverseOrder) comparator = comparator.reversed() - listToSort.sortWith(comparators.first.then(comparator)) - } - - private inner class Comparators { - val first = Comparator { o1, o2 -> - o2.isChecked.compareTo(o1.isChecked) - } - val byLabel = Comparator { o1, o2 -> - val n1 = PackageHelper.loadAppLabel(o1).lowercase(Locale.getDefault()) - val n2 = PackageHelper.loadAppLabel(o2).lowercase(Locale.getDefault()) - Collator.getInstance(Locale.getDefault()).compare(n1, n2) - } - val byPackageName = Comparator { o1, o2 -> - val n1 = o1.lowercase(Locale.getDefault()) - val n2 = o2.lowercase(Locale.getDefault()) - Collator.getInstance(Locale.getDefault()).compare(n1, n2) - } - val byInstallTime = Comparator { o1, o2 -> - val n1 = PackageHelper.loadPackageInfo(o1).firstInstallTime - val n2 = PackageHelper.loadPackageInfo(o2).firstInstallTime - n2.compareTo(n1) - } - val byUpdateTime = Comparator { o1, o2 -> - val n1 = PackageHelper.loadPackageInfo(o1).lastUpdateTime - val n2 = PackageHelper.loadPackageInfo(o2).lastUpdateTime - n2.compareTo(n1) + PackageHelper.sortList(firstComparator) + applyFilter() } } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/TemplateSettingsViewModel.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/TemplateSettingsViewModel.kt index 0509bca9..b93f8fb7 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/TemplateSettingsViewModel.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/TemplateSettingsViewModel.kt @@ -1,13 +1,30 @@ package icu.nullptr.hidemyapplist.ui.viewmodel import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import icu.nullptr.hidemyapplist.service.ConfigManager +import icu.nullptr.hidemyapplist.ui.fragment.TemplateSettingsFragmentArgs import kotlinx.coroutines.flow.MutableStateFlow -class TemplateSettingsViewModel : ViewModel() { +class TemplateSettingsViewModel( + val originalName: String?, + val isWhiteList: Boolean, + var name: String? +) : ViewModel() { - var name: String? = null - var originalName: String? = null - var isWhiteList: Boolean = false + class Factory(private val args: TemplateSettingsFragmentArgs) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(TemplateSettingsViewModel::class.java)) { + val viewModel = TemplateSettingsViewModel(args.name, args.isWhiteList, args.name) + args.name?.let { + viewModel.appliedAppList.value = ConfigManager.getTemplateAppliedAppList(it) + viewModel.targetAppList.value = ConfigManager.getTemplateTargetAppList(it) + } + @Suppress("UNCHECKED_CAST") + return viewModel as T + } else throw IllegalArgumentException("Unknown ViewModel class") + } + } val appliedAppList = MutableStateFlow>(ArrayList()) val targetAppList = MutableStateFlow>(ArrayList()) diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt b/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt index 1b253e6d..3fe8fe19 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt @@ -4,28 +4,59 @@ import android.content.pm.ApplicationInfo import android.content.pm.IPackageManager import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.graphics.Bitmap import icu.nullptr.hidemyapplist.hmaApp +import icu.nullptr.hidemyapplist.service.PrefManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import java.text.Collator +import java.util.* object PackageHelper { class PackageCache( val info: PackageInfo, - val label: String + val label: String, + val icon: Bitmap ) + private object Comparators { + val byLabel = Comparator { o1, o2 -> + val n1 = loadAppLabel(o1).lowercase(Locale.getDefault()) + val n2 = loadAppLabel(o2).lowercase(Locale.getDefault()) + Collator.getInstance(Locale.getDefault()).compare(n1, n2) + } + val byPackageName = Comparator { o1, o2 -> + val n1 = o1.lowercase(Locale.getDefault()) + val n2 = o2.lowercase(Locale.getDefault()) + Collator.getInstance(Locale.getDefault()).compare(n1, n2) + } + val byInstallTime = Comparator { o1, o2 -> + val n1 = loadPackageInfo(o1).firstInstallTime + val n2 = loadPackageInfo(o2).firstInstallTime + n2.compareTo(n1) + } + val byUpdateTime = Comparator { o1, o2 -> + val n1 = loadPackageInfo(o1).lastUpdateTime + val n2 = loadPackageInfo(o2).lastUpdateTime + n2.compareTo(n1) + } + } + private lateinit var ipm: IPackageManager private lateinit var pm: PackageManager - private val _packageCache = MutableSharedFlow>(replay = 1) - val packageCache: SharedFlow> = _packageCache + private val packageCache = MutableSharedFlow>(replay = 1) + private val mAppList = MutableSharedFlow>(replay = 1) + val appList: SharedFlow> = mAppList - private val _isRefreshing = MutableSharedFlow(replay = 1) - val isRefreshing: SharedFlow = _isRefreshing + private val mRefreshing = MutableSharedFlow(replay = 1) + val isRefreshing: SharedFlow = mRefreshing init { // TODO: PackageManagerDelegate @@ -35,30 +66,49 @@ object PackageHelper { fun invalidateCache() { hmaApp.globalScope.launch { - _isRefreshing.emit(true) + mRefreshing.emit(true) val cache = withContext(Dispatchers.IO) { val packages = pm.getInstalledPackages(0) mutableMapOf().also { for (packageInfo in packages) { val label = pm.getApplicationLabel(packageInfo.applicationInfo).toString() - it[packageInfo.packageName] = PackageCache(packageInfo, label) + val icon = hmaApp.appIconLoader.loadIcon(packageInfo.applicationInfo) + it[packageInfo.packageName] = PackageCache(packageInfo, label, icon) } } } - _packageCache.emit(cache) - _isRefreshing.emit(false) + packageCache.emit(cache) + mAppList.emit(cache.keys.toMutableList()) + mRefreshing.emit(false) } } - fun loadPackageInfo(packageName: String): PackageInfo { - return packageCache.replayCache[0][packageName]!!.info + suspend fun sortList(firstComparator: Comparator) { + var comparator = when (PrefManager.filter_sortMethod) { + PrefManager.SortMethod.BY_LABEL -> Comparators.byLabel + PrefManager.SortMethod.BY_PACKAGE_NAME -> Comparators.byPackageName + PrefManager.SortMethod.BY_INSTALL_TIME -> Comparators.byInstallTime + PrefManager.SortMethod.BY_UPDATE_TIME -> Comparators.byUpdateTime + } + if (PrefManager.filter_reverseOrder) comparator = comparator.reversed() + val list = mAppList.first() + list.sortWith(firstComparator.then(comparator)) + mAppList.emit(list) + } + + fun loadPackageInfo(packageName: String): PackageInfo = runBlocking { + packageCache.first()[packageName]!!.info + } + + fun loadAppLabel(packageName: String): String = runBlocking { + packageCache.first()[packageName]!!.label } - fun loadAppLabel(packageName: String): String { - return packageCache.replayCache[0][packageName]!!.label + fun loadAppIcon(packageName: String): Bitmap = runBlocking { + packageCache.first()[packageName]!!.icon } - fun isSystem(packageName: String): Boolean { - return packageCache.replayCache[0][packageName]!!.info.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0 + fun isSystem(packageName: String): Boolean = runBlocking { + packageCache.first()[packageName]!!.info.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0 } } diff --git a/app/src/main/res/layout/app_item_view.xml b/app/src/main/res/layout/app_item_view.xml index 37da6b64..996bdea7 100644 --- a/app/src/main/res/layout/app_item_view.xml +++ b/app/src/main/res/layout/app_item_view.xml @@ -34,8 +34,9 @@ android:layout_height="wrap_content" android:fontFamily="serif" android:text="@string/enabled" - android:textAppearance="?textAppearanceBodyMedium" - android:textColor="?colorPrimary" /> + android:textColor="?colorPrimary" + android:visibility="gone" + tools:visibility="visible" /> - - - diff --git a/app/src/main/res/layout/fragment_app_select.xml b/app/src/main/res/layout/fragment_app_select.xml index f7f967f4..3ab7c343 100644 --- a/app/src/main/res/layout/fragment_app_select.xml +++ b/app/src/main/res/layout/fragment_app_select.xml @@ -4,17 +4,19 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:transitionGroup="true" tools:context="icu.nullptr.hidemyapplist.ui.fragment.AppSelectFragment"> + android:layout_height="wrap_content"> + android:layout_height="?actionBarSize" + android:elevation="0dp" /> - - diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 281ce1f5..0bbcd0fe 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -5,14 +5,15 @@ android:layout_height="match_parent"> + android:layout_height="wrap_content"> + android:layout_height="?actionBarSize" + android:elevation="0dp" /> - diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 4dc24ba1..d25a98b8 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,9 +3,9 @@ 2dp 12dp 20dp - 15dp + 16dp 28dp - 36dp + 42dp 6dp 1dp diff --git a/build.gradle.kts b/build.gradle.kts index 499ba0ad..4b82de20 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ buildscript { dependencies { classpath(kotlin("gradle-plugin", version = "1.7.10")) classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.5.1") - classpath("com.android.tools.build:gradle:7.2.1") + classpath("com.android.tools.build:gradle:7.2.2") classpath("com.google.gms:google-services:4.3.13") } } diff --git a/gradle.properties b/gradle.properties index f1a4ddc0..58785fc4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ android.nonTransitiveRClass=true android.enableR8.fullMode=true android.useAndroidX=true -agpVersion=7.2.1 +agpVersion=7.2.2