Cherish: improve app list fragment
* properly constrained image view and max lines of both label and package views * use lifecycle scope inherent to fragments instead of creating new coroutine scopes * fetch package list from pm every time refreshList is called * early release locks when refreshing list * properly annotate setDisplayCategory method * keep AppListAdapter as a regular nested class instead of an inner class for reusability Signed-off-by: jhonboy121 <alfredmathew05@gmail.com> Signed-off-by: Hưng Phan <phandinhhungvp2001@gmail.com>
This commit is contained in:
@@ -25,11 +25,11 @@
|
|||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginVertical="4dp"
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:scaleType="centerInside"
|
android:scaleType="centerInside"
|
||||||
settings:layout_constraintStart_toStartOf="parent"
|
settings:layout_constraintStart_toStartOf="parent"
|
||||||
settings:layout_constraintTop_toTopOf="parent" />
|
settings:layout_constraintTop_toTopOf="parent"
|
||||||
|
settings:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/label"
|
android:id="@+id/label"
|
||||||
@@ -38,23 +38,26 @@
|
|||||||
android:layout_marginStart="@dimen/default_margin"
|
android:layout_marginStart="@dimen/default_margin"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:maxLines="1"
|
||||||
settings:layout_constraintStart_toEndOf="@id/icon"
|
settings:layout_constraintStart_toEndOf="@id/icon"
|
||||||
settings:layout_constraintEnd_toStartOf="@id/checkBox"
|
settings:layout_constraintEnd_toStartOf="@id/check_box"
|
||||||
settings:layout_constraintTop_toTopOf="@id/icon" />
|
settings:layout_constraintTop_toTopOf="@id/icon"
|
||||||
|
settings:layout_constraintBottom_toTopOf="@id/package_name" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/packageName"
|
android:id="@+id/package_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:maxLines="2"
|
||||||
settings:layout_constraintStart_toStartOf="@id/label"
|
settings:layout_constraintStart_toStartOf="@id/label"
|
||||||
settings:layout_constraintEnd_toEndOf="@id/label"
|
settings:layout_constraintEnd_toEndOf="@id/label"
|
||||||
settings:layout_constraintTop_toBottomOf="@id/label"
|
settings:layout_constraintTop_toBottomOf="@id/label"
|
||||||
settings:layout_constraintBottom_toBottomOf="@id/icon" />
|
settings:layout_constraintBottom_toBottomOf="@id/icon" />
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/checkBox"
|
android:id="@+id/check_box"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.cherish.settings.fragment
|
package com.cherish.settings.fragment
|
||||||
|
|
||||||
import android.content.Context
|
import android.annotation.IntDef
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
@@ -35,6 +35,7 @@ import android.widget.TextView
|
|||||||
|
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
@@ -43,8 +44,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@@ -57,38 +56,29 @@ import kotlinx.coroutines.withContext
|
|||||||
* and package name of the application, along with a [CheckBox]
|
* and package name of the application, along with a [CheckBox]
|
||||||
* indicating whether the item is selected or not.
|
* indicating whether the item is selected or not.
|
||||||
*/
|
*/
|
||||||
abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnActionExpandListener {
|
abstract class AppListFragment : Fragment(R.layout.app_list_layout),
|
||||||
|
MenuItem.OnActionExpandListener {
|
||||||
|
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
private lateinit var fragmentScope: CoroutineScope
|
private lateinit var pm: PackageManager
|
||||||
private lateinit var progressBar: ProgressBar
|
|
||||||
private lateinit var appBarLayout: AppBarLayout
|
|
||||||
private lateinit var packageManager: PackageManager
|
|
||||||
private lateinit var recyclerView: RecyclerView
|
|
||||||
private lateinit var adapter: AppListAdapter
|
private lateinit var adapter: AppListAdapter
|
||||||
|
|
||||||
private val packageList = mutableListOf<PackageInfo>()
|
private var appBarLayout: AppBarLayout? = null
|
||||||
|
private var recyclerView: RecyclerView? = null
|
||||||
|
private var progressBar: ProgressBar? = null
|
||||||
|
|
||||||
private var searchText = ""
|
private var searchText = ""
|
||||||
private var displayCategory: Int = CATEGORY_USER_ONLY
|
private var displayCategory: Int = CATEGORY_USER_ONLY
|
||||||
private var packageFilter: ((PackageInfo) -> Boolean) = { true }
|
private var packageFilter: (PackageInfo) -> Boolean = { true }
|
||||||
private var packageComparator: ((PackageInfo, PackageInfo) -> Int) = { a, b ->
|
private var packageComparator: (PackageInfo, PackageInfo) -> Int = { first, second ->
|
||||||
getLabel(a).compareTo(getLabel(b))
|
getLabel(first).compareTo(getLabel(second))
|
||||||
}
|
|
||||||
|
|
||||||
private var needsToHideProgressBar = false
|
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
|
||||||
super.onAttach(context)
|
|
||||||
fragmentScope = CoroutineScope(Dispatchers.Main)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
packageManager = requireContext().packageManager
|
pm = requireContext().packageManager
|
||||||
packageList.addAll(packageManager.getInstalledPackages(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,15 +87,19 @@ abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnA
|
|||||||
abstract protected fun getTitle(): Int
|
abstract protected fun getTitle(): Int
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
requireActivity().setTitle(getTitle())
|
val activity = requireActivity()
|
||||||
appBarLayout = requireActivity().findViewById(R.id.app_bar)
|
activity.setTitle(getTitle())
|
||||||
|
appBarLayout = activity.findViewById(R.id.app_bar)
|
||||||
progressBar = view.findViewById(R.id.loading_progress)
|
progressBar = view.findViewById(R.id.loading_progress)
|
||||||
adapter = AppListAdapter()
|
adapter = AppListAdapter(getInitialCheckedList(), layoutInflater).apply {
|
||||||
recyclerView = view.findViewById<RecyclerView>(R.id.apps_list).also {
|
setOnAppSelectListener { onAppSelected(it) }
|
||||||
|
setOnAppDeselectListener { onAppDeselected(it) }
|
||||||
|
setOnListUpdateListener { onListUpdate(it) }
|
||||||
|
}
|
||||||
|
recyclerView = view.findViewById<RecyclerView>(R.id.apps_list)?.also {
|
||||||
it.layoutManager = LinearLayoutManager(context)
|
it.layoutManager = LinearLayoutManager(context)
|
||||||
it.adapter = adapter
|
it.adapter = adapter
|
||||||
}
|
}
|
||||||
needsToHideProgressBar = true
|
|
||||||
refreshList()
|
refreshList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,19 +112,21 @@ abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnA
|
|||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.app_list_menu, menu)
|
inflater.inflate(R.menu.app_list_menu, menu)
|
||||||
val searchItem = menu.findItem(R.id.search).also {
|
val searchItem = menu.findItem(R.id.search).also {
|
||||||
it.setOnActionExpandListener(this)
|
if (appBarLayout != null) {
|
||||||
|
it.setOnActionExpandListener(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val searchView = searchItem.actionView as SearchView
|
val searchView = searchItem.actionView as SearchView
|
||||||
searchView.setQueryHint(getString(R.string.search_apps));
|
searchView.setQueryHint(getString(R.string.search_apps))
|
||||||
searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener {
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
override fun onQueryTextSubmit(query: String) = false
|
override fun onQueryTextSubmit(query: String) = false
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String): Boolean {
|
override fun onQueryTextChange(newText: String): Boolean {
|
||||||
fragmentScope.launch {
|
lifecycleScope.launch {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
searchText = newText
|
searchText = newText
|
||||||
}
|
}
|
||||||
refreshList()
|
refreshListInternal()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -139,34 +135,29 @@ abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnA
|
|||||||
|
|
||||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||||
// To prevent a large space on tool bar.
|
// To prevent a large space on tool bar.
|
||||||
appBarLayout.setExpanded(false /*expanded*/, false /*animate*/)
|
appBarLayout?.setExpanded(false /*expanded*/, false /*animate*/)
|
||||||
// To prevent user expanding the collapsing tool bar view.
|
// To prevent user expanding the collapsing tool bar view.
|
||||||
ViewCompat.setNestedScrollingEnabled(recyclerView, false)
|
recyclerView?.let { ViewCompat.setNestedScrollingEnabled(it, false) }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||||
// We keep the collapsed status after user cancel the search function.
|
// We keep the collapsed status after user cancel the search function.
|
||||||
appBarLayout.setExpanded(false /*expanded*/, false /*animate*/)
|
appBarLayout?.setExpanded(false /*expanded*/, false /*animate*/)
|
||||||
// Allow user to expande the tool bar view.
|
// Allow user to expande the tool bar view.
|
||||||
ViewCompat.setNestedScrollingEnabled(recyclerView, true)
|
recyclerView?.let { ViewCompat.setNestedScrollingEnabled(it, true) }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetach() {
|
|
||||||
fragmentScope.cancel()
|
|
||||||
super.onDetach()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the type of apps that should be displayed in the list.
|
* Set the type of apps that should be displayed in the list.
|
||||||
* Defaults to [CATEGORY_USER_ONLY].
|
* Defaults to [CATEGORY_USER_ONLY].
|
||||||
*
|
*
|
||||||
* @param category one of [CATEGORY_SYSTEM_ONLY],
|
* @param category one of [CATEGORY_SYSTEM_ONLY],
|
||||||
* [CATEGORY_USER_ONLY], [CATEGORY_BOTH]
|
* [CATEGORY_USER_ONLY], [CATEGORY_BOTH]
|
||||||
*/
|
*/
|
||||||
fun setDisplayCategory(category: Int) {
|
fun setDisplayCategory(@Category category: Int) {
|
||||||
fragmentScope.launch {
|
lifecycleScope.launch {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
displayCategory = category
|
displayCategory = category
|
||||||
}
|
}
|
||||||
@@ -177,10 +168,10 @@ abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnA
|
|||||||
* Set a custom filter to filter out items from the list.
|
* Set a custom filter to filter out items from the list.
|
||||||
*
|
*
|
||||||
* @param customFilter a function that takes a [PackageInfo] and
|
* @param customFilter a function that takes a [PackageInfo] and
|
||||||
* returns a [Boolean] indicating whether to show the item or not.
|
* returns a [Boolean] indicating whether to show the item or not.
|
||||||
*/
|
*/
|
||||||
fun setCustomFilter(customFilter: ((packageInfo: PackageInfo) -> Boolean)) {
|
fun setCustomFilter(customFilter: (PackageInfo) -> Boolean) {
|
||||||
fragmentScope.launch {
|
lifecycleScope.launch {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
packageFilter = customFilter
|
packageFilter = customFilter
|
||||||
}
|
}
|
||||||
@@ -188,13 +179,13 @@ abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnA
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a [Comparator] for sorting the elements in the list..
|
* Set a [Comparator] for sorting the elements in the list.
|
||||||
*
|
*
|
||||||
* @param comparator a function that takes two [PackageInfo]'s and returns
|
* @param comparator a function that takes two [PackageInfo]'s and returns
|
||||||
* an [Int] representing their relative priority.
|
* an [Int] representing their relative priority.
|
||||||
*/
|
*/
|
||||||
fun setComparator(comparator: ((a: PackageInfo, b: PackageInfo) -> Int)) {
|
fun setComparator(comparator: (PackageInfo, PackageInfo) -> Int) {
|
||||||
fragmentScope.launch {
|
lifecycleScope.launch {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
packageComparator = comparator
|
packageComparator = comparator
|
||||||
}
|
}
|
||||||
@@ -222,75 +213,111 @@ abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnA
|
|||||||
*/
|
*/
|
||||||
open protected fun onAppDeselected(packageName: String) {}
|
open protected fun onAppDeselected(packageName: String) {}
|
||||||
|
|
||||||
protected fun refreshList() {
|
fun refreshList() {
|
||||||
fragmentScope.launch {
|
lifecycleScope.launch {
|
||||||
val list = withContext(Dispatchers.Default) {
|
refreshListInternal()
|
||||||
mutex.withLock {
|
|
||||||
packageList.filter {
|
|
||||||
when (displayCategory) {
|
|
||||||
CATEGORY_SYSTEM_ONLY -> it.applicationInfo.isSystemApp()
|
|
||||||
CATEGORY_USER_ONLY -> !it.applicationInfo.isSystemApp()
|
|
||||||
else -> true
|
|
||||||
} &&
|
|
||||||
getLabel(it).contains(searchText, true) &&
|
|
||||||
packageFilter(it)
|
|
||||||
}.sortedWith(packageComparator).map { appInfofromPackage(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
adapter.submitList(list)
|
|
||||||
if (needsToHideProgressBar) {
|
|
||||||
progressBar.visibility = View.GONE
|
|
||||||
needsToHideProgressBar = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun appInfofromPackage(packageInfo: PackageInfo): AppInfo =
|
private suspend fun refreshListInternal() {
|
||||||
AppInfo(
|
val list = withContext(Dispatchers.Default) {
|
||||||
packageInfo.packageName,
|
val sortedList = mutex.withLock {
|
||||||
getLabel(packageInfo),
|
pm.getInstalledPackages(PackageManager.MATCH_ALL).filter {
|
||||||
packageInfo.applicationInfo.loadIcon(packageManager),
|
val categoryMatches = when (displayCategory) {
|
||||||
)
|
CATEGORY_SYSTEM_ONLY -> it.applicationInfo.isSystemApp()
|
||||||
|
CATEGORY_USER_ONLY -> !it.applicationInfo.isSystemApp()
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
categoryMatches && packageFilter(it) &&
|
||||||
|
getLabel(it).contains(searchText, true)
|
||||||
|
}.sortedWith(packageComparator)
|
||||||
|
}
|
||||||
|
sortedList.map {
|
||||||
|
AppInfo(
|
||||||
|
it.packageName,
|
||||||
|
getLabel(it),
|
||||||
|
it.applicationInfo.loadIcon(pm),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.submitList(list)
|
||||||
|
progressBar?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
private fun getLabel(packageInfo: PackageInfo) =
|
private fun getLabel(packageInfo: PackageInfo) =
|
||||||
packageInfo.applicationInfo.loadLabel(packageManager).toString()
|
packageInfo.applicationInfo.loadLabel(pm).toString()
|
||||||
|
|
||||||
private inner class AppListAdapter :
|
private class AppListAdapter(
|
||||||
ListAdapter<AppInfo, AppListViewHolder>(itemCallback)
|
initialCheckedList: List<String>,
|
||||||
{
|
private val layoutInflater: LayoutInflater
|
||||||
private val checkedList = getInitialCheckedList().toMutableList()
|
) : ListAdapter<AppInfo, AppListViewHolder>(itemCallback) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
private val checkedList = initialCheckedList.toMutableList()
|
||||||
AppListViewHolder(layoutInflater.inflate(
|
private var appSelectListener: (String) -> Unit = {}
|
||||||
R.layout.app_list_item, parent, false))
|
private var appDeselectListener: (String) -> Unit = {}
|
||||||
|
private var listUpdateListener: (List<String>) -> Unit = {}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int
|
||||||
|
) = AppListViewHolder(
|
||||||
|
layoutInflater.inflate(
|
||||||
|
R.layout.app_list_item,
|
||||||
|
parent,
|
||||||
|
false /* attachToParent */
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: AppListViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: AppListViewHolder, position: Int) {
|
||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
val pkg = item.packageName
|
holder.label.text = item.label
|
||||||
holder.label.setText(item.label)
|
holder.packageName.text = item.packageName
|
||||||
holder.packageName.setText(pkg)
|
|
||||||
holder.icon.setImageDrawable(item.icon)
|
holder.icon.setImageDrawable(item.icon)
|
||||||
holder.checkBox.setChecked(checkedList.contains(pkg))
|
holder.checkBox.isChecked = checkedList.contains(item.packageName)
|
||||||
holder.itemView.setOnClickListener {
|
holder.itemView.setOnClickListener {
|
||||||
if (checkedList.contains(pkg)){
|
if (checkedList.contains(item.packageName)) {
|
||||||
checkedList.remove(pkg)
|
checkedList.remove(item.packageName)
|
||||||
onAppDeselected(pkg)
|
appDeselectListener(item.packageName)
|
||||||
} else {
|
} else {
|
||||||
checkedList.add(pkg)
|
checkedList.add(item.packageName)
|
||||||
onAppSelected(pkg)
|
appSelectListener(item.packageName)
|
||||||
}
|
}
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
onListUpdate(checkedList.toList())
|
listUpdateListener(checkedList.toList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnAppSelectListener(listener: (String) -> Unit) {
|
||||||
|
appSelectListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnAppDeselectListener(listener: (String) -> Unit) {
|
||||||
|
appDeselectListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnListUpdateListener(listener: (List<String>) -> Unit) {
|
||||||
|
listUpdateListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val itemCallback = object : DiffUtil.ItemCallback<AppInfo>() {
|
||||||
|
override fun areItemsTheSame(oldInfo: AppInfo, newInfo: AppInfo) =
|
||||||
|
oldInfo.packageName == newInfo.packageName
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldInfo: AppInfo, newInfo: AppInfo) =
|
||||||
|
oldInfo == newInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AppListViewHolder(itemView: View) :
|
private class AppListViewHolder(
|
||||||
RecyclerView.ViewHolder(itemView) {
|
itemView: View
|
||||||
|
) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
val icon: ImageView = itemView.findViewById(R.id.icon)
|
val icon: ImageView = itemView.findViewById(R.id.icon)
|
||||||
val label: TextView = itemView.findViewById(R.id.label)
|
val label: TextView = itemView.findViewById(R.id.label)
|
||||||
val packageName: TextView = itemView.findViewById(R.id.packageName)
|
val packageName: TextView = itemView.findViewById(R.id.package_name)
|
||||||
val checkBox: CheckBox = itemView.findViewById(R.id.checkBox)
|
val checkBox: CheckBox = itemView.findViewById(R.id.check_box)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class AppInfo(
|
private data class AppInfo(
|
||||||
@@ -300,18 +327,16 @@ abstract class AppListFragment: Fragment(R.layout.app_list_layout), MenuItem.OnA
|
|||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "AppListFragment"
|
|
||||||
|
|
||||||
const val CATEGORY_SYSTEM_ONLY = 0
|
const val CATEGORY_SYSTEM_ONLY = 0
|
||||||
const val CATEGORY_USER_ONLY = 1
|
const val CATEGORY_USER_ONLY = 1
|
||||||
const val CATEGORY_BOTH = 2
|
const val CATEGORY_BOTH = 2
|
||||||
|
|
||||||
private val itemCallback = object : DiffUtil.ItemCallback<AppInfo>() {
|
@IntDef(value = intArrayOf(
|
||||||
override fun areItemsTheSame(oldInfo: AppInfo, newInfo: AppInfo) =
|
CATEGORY_SYSTEM_ONLY,
|
||||||
oldInfo.packageName == newInfo.packageName
|
CATEGORY_USER_ONLY,
|
||||||
|
CATEGORY_BOTH
|
||||||
override fun areContentsTheSame(oldInfo: AppInfo, newInfo: AppInfo) =
|
))
|
||||||
oldInfo == newInfo
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
}
|
annotation class Category
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user