1 /** <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * ``` 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * ``` 10 * 11 * Unless required by applicable law or agreed to in writing, software distributed under the License 12 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 * or implied. See the License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.android.healthconnect.controller.permissiontypes.prioritylist 17 18 import android.annotation.SuppressLint 19 import android.graphics.drawable.Drawable 20 import android.view.LayoutInflater 21 import android.view.MotionEvent 22 import android.view.View 23 import android.view.ViewGroup 24 import android.widget.ImageView 25 import android.widget.TextView 26 import androidx.recyclerview.widget.ItemTouchHelper 27 import androidx.recyclerview.widget.RecyclerView 28 import com.android.healthconnect.controller.R 29 import com.android.healthconnect.controller.permissiontypes.HealthPermissionTypesViewModel 30 import com.android.healthconnect.controller.shared.app.AppMetadata 31 import java.text.NumberFormat 32 33 /** RecyclerView adapter that holds the list of the priority list. */ 34 class PriorityListAdapter( 35 appMetadataList: List<AppMetadata>, 36 private val viewModel: HealthPermissionTypesViewModel 37 ) : RecyclerView.Adapter<PriorityListAdapter.PriorityListItemViewHolder?>() { 38 39 private val POSITION_CHANGED_PAYLOAD = Any() 40 41 private var listener: ItemTouchHelper? = null 42 private var appMetadataList = appMetadataList.toMutableList() 43 44 override fun onCreateViewHolder( 45 viewGroup: ViewGroup, 46 viewType: Int 47 ): PriorityListItemViewHolder { 48 return PriorityListItemViewHolder( 49 LayoutInflater.from(viewGroup.context) 50 .inflate(R.layout.priority_item, viewGroup, false), 51 listener) 52 } 53 54 override fun onBindViewHolder(viewHolder: PriorityListItemViewHolder, position: Int) { 55 // This method is needed as it's marked as abstract however it's empty as the logic is 56 // handled in the onBindViewHolder method that accepts a payloads list. 57 } 58 59 override fun onBindViewHolder( 60 viewHolder: PriorityListItemViewHolder, 61 position: Int, 62 payloads: List<Any> 63 ) { 64 viewHolder.bindPosition(position) 65 66 // Only bind the full view when there's no payload objects to avoid re-binding the icon and 67 // touch listener in the middle of a drag event. 68 if (payloads.isEmpty()) { 69 viewHolder.bind(appMetadataList[position].appName, appMetadataList[position].icon) 70 } 71 } 72 73 override fun getItemCount(): Int { 74 return appMetadataList.size 75 } 76 77 fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { 78 val movedAppInfo: AppMetadata = appMetadataList.removeAt(fromPosition) 79 appMetadataList.add( 80 if (toPosition > fromPosition + 1) toPosition - 1 else toPosition, movedAppInfo) 81 notifyItemMoved(fromPosition, toPosition) 82 if (toPosition < fromPosition) { 83 notifyItemRangeChanged( 84 toPosition, fromPosition - toPosition + 1, POSITION_CHANGED_PAYLOAD) 85 } else { 86 notifyItemRangeChanged( 87 fromPosition, toPosition - fromPosition + 1, POSITION_CHANGED_PAYLOAD) 88 } 89 viewModel.setEditedPriorityList(appMetadataList) 90 return true 91 } 92 93 fun getPackageNameList(): List<String> { 94 return appMetadataList.stream().map(AppMetadata::packageName).toList() 95 } 96 97 fun setOnItemDragStartedListener(listener: ItemTouchHelper) { 98 this.listener = listener 99 } 100 101 fun removeOnItemDragStartedListener() { 102 listener = null 103 } 104 105 /** Shows a single item of the priority list. */ 106 class PriorityListItemViewHolder(itemView: View, onItemDragStartedListener: ItemTouchHelper?) : 107 RecyclerView.ViewHolder(itemView) { 108 private val appPositionView: TextView 109 private val appNameView: TextView 110 private val appIconView: ImageView 111 private val dragIconView: View 112 113 private val onItemDragStartedListener: ItemTouchHelper? 114 115 init { 116 appPositionView = itemView.findViewById(R.id.app_position) 117 appNameView = itemView.findViewById(R.id.app_name) 118 appIconView = itemView.findViewById(R.id.app_icon) 119 dragIconView = itemView.findViewById(R.id.action_icon) 120 this.onItemDragStartedListener = onItemDragStartedListener 121 } 122 123 // These items are not clickable and so onTouch does not need to reimplement click 124 // conditions. 125 // Drag&drop in accessibility mode (talk back) is implemented as custom actions. 126 @SuppressLint("ClickableViewAccessibility") 127 fun bind(appName: String?, appIcon: Drawable?) { 128 appNameView.text = appName 129 appIconView.setImageDrawable(appIcon) 130 dragIconView.setOnTouchListener { _, event -> 131 if (event.action == MotionEvent.ACTION_DOWN || 132 event.action == MotionEvent.ACTION_UP) { 133 onItemDragStartedListener?.startDrag(this) 134 } 135 false 136 } 137 } 138 139 fun bindPosition(appPosition: Int) { 140 // Adding 1 to position as position starts from 0 but should show to the user starting 141 // from 1. 142 val positionString: String = NumberFormat.getIntegerInstance().format(appPosition + 1) 143 appPositionView.text = positionString 144 } 145 } 146 } 147