1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package com.android.wallpaper.picker.option.ui.adapter 19 20 import android.view.LayoutInflater 21 import android.view.View 22 import android.view.ViewGroup 23 import androidx.annotation.LayoutRes 24 import androidx.lifecycle.LifecycleOwner 25 import androidx.lifecycle.lifecycleScope 26 import androidx.recyclerview.widget.DiffUtil 27 import androidx.recyclerview.widget.RecyclerView 28 import com.android.wallpaper.R 29 import com.android.wallpaper.picker.option.ui.binder.OptionItemBinder 30 import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel 31 import kotlinx.coroutines.CoroutineDispatcher 32 import kotlinx.coroutines.Dispatchers 33 import kotlinx.coroutines.DisposableHandle 34 import kotlinx.coroutines.launch 35 import kotlinx.coroutines.withContext 36 37 /** Adapts between option items and their views. */ 38 class OptionItemAdapter<T>( 39 @LayoutRes private val layoutResourceId: Int, 40 private val lifecycleOwner: LifecycleOwner, 41 private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO, 42 private val foregroundTintSpec: OptionItemBinder.TintSpec? = null, 43 private val bindIcon: (View, T) -> Unit, 44 ) : RecyclerView.Adapter<OptionItemAdapter.ViewHolder>() { 45 46 private val items = mutableListOf<OptionItemViewModel<T>>() 47 setItemsnull48 fun setItems(items: List<OptionItemViewModel<T>>, callback: (() -> Unit)? = null) { 49 lifecycleOwner.lifecycleScope.launch { 50 val oldItems = this@OptionItemAdapter.items 51 val newItems = items 52 val diffResult = 53 withContext(backgroundDispatcher) { 54 DiffUtil.calculateDiff( 55 object : DiffUtil.Callback() { 56 override fun getOldListSize(): Int { 57 return oldItems.size 58 } 59 60 override fun getNewListSize(): Int { 61 return newItems.size 62 } 63 64 override fun areItemsTheSame( 65 oldItemPosition: Int, 66 newItemPosition: Int 67 ): Boolean { 68 val oldItem = oldItems[oldItemPosition] 69 val newItem = newItems[newItemPosition] 70 return oldItem.key.value == newItem.key.value 71 } 72 73 override fun areContentsTheSame( 74 oldItemPosition: Int, 75 newItemPosition: Int 76 ): Boolean { 77 val oldItem = oldItems[oldItemPosition] 78 val newItem = newItems[newItemPosition] 79 return oldItem == newItem 80 } 81 }, 82 /* detectMoves= */ false, 83 ) 84 } 85 86 oldItems.clear() 87 oldItems.addAll(items) 88 diffResult.dispatchUpdatesTo(this@OptionItemAdapter) 89 if (callback != null) { 90 callback() 91 } 92 } 93 } 94 95 class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 96 var disposableHandle: DisposableHandle? = null 97 } 98 getItemCountnull99 override fun getItemCount(): Int { 100 return items.size 101 } 102 onCreateViewHoldernull103 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 104 return ViewHolder( 105 LayoutInflater.from(parent.context) 106 .inflate( 107 layoutResourceId, 108 parent, 109 false, 110 ) 111 ) 112 } 113 onBindViewHoldernull114 override fun onBindViewHolder(holder: ViewHolder, position: Int) { 115 holder.disposableHandle?.dispose() 116 val item = items[position] 117 item.payload?.let { 118 bindIcon(holder.itemView.requireViewById(R.id.foreground), item.payload) 119 } 120 holder.disposableHandle = 121 OptionItemBinder.bind( 122 view = holder.itemView, 123 viewModel = item, 124 lifecycleOwner = lifecycleOwner, 125 foregroundTintSpec = foregroundTintSpec, 126 ) 127 } 128 } 129