1 /* 2 * Copyright (C) 2024 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.picker.customization.ui.viewmodel.ColorUpdateViewModel 29 import com.android.wallpaper.picker.option.ui.binder.OptionItemBinder2 30 import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel2 31 import java.lang.ref.WeakReference 32 import kotlinx.coroutines.CoroutineDispatcher 33 import kotlinx.coroutines.Dispatchers 34 import kotlinx.coroutines.DisposableHandle 35 import kotlinx.coroutines.Job 36 import kotlinx.coroutines.launch 37 import kotlinx.coroutines.withContext 38 39 /** Adapts between option items and their views. */ 40 class OptionItemAdapter2<T>( 41 @LayoutRes private val layoutResourceId: Int, 42 private val lifecycleOwner: LifecycleOwner, 43 private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO, 44 private val bindPayload: (View, T) -> DisposableHandle?, 45 private val colorUpdateViewModel: WeakReference<ColorUpdateViewModel>, 46 private val shouldAnimateColor: () -> Boolean, 47 ) : RecyclerView.Adapter<OptionItemAdapter2.ViewHolder>() { 48 49 private val items = mutableListOf<OptionItemViewModel2<T>>() 50 private var setItemsJob: Job? = null 51 setItemsnull52 fun setItems(items: List<OptionItemViewModel2<T>>, callback: (() -> Unit)? = null) { 53 setItemsJob?.cancel() 54 setItemsJob = 55 lifecycleOwner.lifecycleScope.launch { 56 val oldItems = this@OptionItemAdapter2.items 57 val newItems = items 58 val diffResult = 59 withContext(backgroundDispatcher) { 60 DiffUtil.calculateDiff( 61 object : DiffUtil.Callback() { 62 override fun getOldListSize(): Int { 63 return oldItems.size 64 } 65 66 override fun getNewListSize(): Int { 67 return newItems.size 68 } 69 70 override fun areItemsTheSame( 71 oldItemPosition: Int, 72 newItemPosition: Int, 73 ): Boolean { 74 val oldItem = oldItems[oldItemPosition] 75 val newItem = newItems[newItemPosition] 76 return oldItem.key.value == newItem.key.value 77 } 78 79 override fun areContentsTheSame( 80 oldItemPosition: Int, 81 newItemPosition: Int, 82 ): Boolean { 83 val oldItem = oldItems[oldItemPosition] 84 val newItem = newItems[newItemPosition] 85 return oldItem == newItem 86 } 87 }, 88 /* detectMoves= */ false, 89 ) 90 } 91 92 oldItems.clear() 93 oldItems.addAll(items) 94 diffResult.dispatchUpdatesTo(this@OptionItemAdapter2) 95 if (callback != null) { 96 callback() 97 } 98 } 99 } 100 101 class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 102 var disposableHandle: DisposableHandle? = null 103 var payloadDisposableHandle: DisposableHandle? = null 104 } 105 getItemCountnull106 override fun getItemCount(): Int { 107 return items.size 108 } 109 onCreateViewHoldernull110 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 111 return ViewHolder( 112 LayoutInflater.from(parent.context).inflate(layoutResourceId, parent, false) 113 ) 114 } 115 onBindViewHoldernull116 override fun onBindViewHolder(holder: ViewHolder, position: Int) { 117 holder.disposableHandle?.dispose() 118 holder.payloadDisposableHandle?.dispose() 119 val item = items[position] 120 holder.payloadDisposableHandle = 121 item.payload?.let { bindPayload(holder.itemView, item.payload) } 122 holder.disposableHandle = 123 OptionItemBinder2.bind( 124 view = holder.itemView, 125 viewModel = item, 126 lifecycleOwner = lifecycleOwner, 127 colorUpdateViewModel = colorUpdateViewModel, 128 shouldAnimateColor = shouldAnimateColor, 129 ) 130 } 131 } 132