1 /* <lambda>null2 * Copyright 2019 Google LLC 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 * https://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 package com.mobileer.androidfxlab 18 19 import android.view.LayoutInflater 20 import android.view.MotionEvent 21 import android.view.View 22 import android.view.ViewGroup 23 import android.widget.LinearLayout 24 import android.widget.SeekBar 25 import android.widget.TextView 26 import androidx.recyclerview.widget.ItemTouchHelper 27 import androidx.recyclerview.widget.RecyclerView 28 import com.google.android.material.switchmaterial.SwitchMaterial 29 import com.mobileer.androidfxlab.datatype.Effect 30 import java.util.* 31 import kotlin.concurrent.timerTask 32 import kotlin.math.max 33 import kotlin.math.min 34 35 object EffectsAdapter : 36 RecyclerView.Adapter<EffectsAdapter.EffectsHolder>() { 37 val effectList = arrayListOf<Effect>() 38 lateinit var mRecyclerView: RecyclerView 39 40 // This class adapts view in effect_view.xml for Effect class 41 class EffectsHolder(val parentView: ViewGroup) : RecyclerView.ViewHolder(parentView) { 42 private val layoutContainer: LinearLayout = parentView.findViewById(R.id.effectContainer) 43 lateinit var effect: Effect 44 private val floatFormat = "%4.2f" 45 private var index: Int = -1 46 fun bindEffect(bindedEffect: Effect, position: Int) { 47 effect = bindedEffect 48 index = position 49 // Clear all views 50 layoutContainer.removeAllViews() 51 View.inflate(layoutContainer.context, 52 R.layout.effect_header, layoutContainer) 53 val header: LinearLayout = layoutContainer.findViewById(R.id.effectHeader) 54 val dragHandleView: View = header.findViewById(R.id.cat_card_list_item_drag_handle) 55 val checkBoxView: SwitchMaterial = header.findViewById(R.id.effectEnabled) 56 val label: TextView = header.findViewById(R.id.effectLabel) 57 // Bind header views 58 label.text = effect.name 59 checkBoxView.isChecked = effectList[index].enable 60 checkBoxView.setOnCheckedChangeListener { _, checked -> 61 effectList[index].enable = checked 62 NativeInterface.enableEffectAt(checked, index) 63 } 64 dragHandleView.setOnTouchListener { _, event -> 65 if (event.action == MotionEvent.ACTION_DOWN) { 66 itemTouchHelper.startDrag(this@EffectsHolder) 67 return@setOnTouchListener true 68 } 69 false 70 } 71 header.setOnTouchListener { _, event -> 72 if (event.action == MotionEvent.ACTION_DOWN) { 73 itemTouchHelper.startSwipe(this@EffectsHolder) 74 return@setOnTouchListener true 75 } 76 false 77 } 78 // Add correct number of SeekBars based on effect 79 for (ind in effect.effectDescription.paramValues.withIndex()) { 80 val param = ind.value 81 val counter = ind.index 82 val view = View.inflate(layoutContainer.context, 83 R.layout.param_seek, null) 84 layoutContainer.addView(view) 85 val paramWrapper: LinearLayout = view.findViewById(R.id.paramWrapper) 86 val paramLabelView: TextView = paramWrapper.findViewById(R.id.paramLabel) 87 val minLabelView: TextView = paramWrapper.findViewById(R.id.minLabel) 88 val maxLabelView: TextView = paramWrapper.findViewById(R.id.maxLabel) 89 val curLabelView: TextView = paramWrapper.findViewById(R.id.curLabel) 90 val seekBar: SeekBar = paramWrapper.findViewById(R.id.seekBar) 91 paramLabelView.text = param.paramName 92 minLabelView.text = floatFormat.format(param.minValue) 93 maxLabelView.text = floatFormat.format(param.maxValue) 94 seekBar.progress = 95 ((param.defaultValue - param.minValue) * 100 / (param.maxValue - param.minValue)).toInt() 96 curLabelView.text = floatFormat.format(param.defaultValue) 97 // Bind param listeners to effects 98 seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 99 val paramInd = counter 100 var timer : Timer? = null 101 102 override fun onStartTrackingTouch(p0: SeekBar?) {} 103 104 override fun onStopTrackingTouch(seekbar: SeekBar?) {} 105 106 override fun onProgressChanged( 107 seekBar: SeekBar?, progress: Int, fromUser: Boolean 108 ) { 109 val fracprogress = 110 ((seekBar!!.progress / 100f) * (param.maxValue - param.minValue) + param.minValue) 111 curLabelView.text = floatFormat.format(fracprogress) 112 113 timer?.cancel() 114 timer = Timer() 115 timer?.schedule(timerTask { updateEffectParam(fracprogress) }, 100) 116 } 117 118 fun updateEffectParam(fracprogress : Float){ 119 effectList[index].paramValues[paramInd] = fracprogress 120 NativeInterface.updateParamsAt(effect, index) 121 } 122 }) 123 } 124 } 125 126 } 127 128 override fun onCreateViewHolder( 129 parent: ViewGroup, 130 viewType: Int 131 ): EffectsHolder { 132 val myView = LayoutInflater.from(parent.context) 133 .inflate(R.layout.effect_view, parent, false) 134 return EffectsHolder(myView as ViewGroup) 135 } 136 137 override fun onBindViewHolder(holder: EffectsHolder, position: Int) { 138 holder.bindEffect(effectList[position], position) 139 } 140 141 override fun getItemCount() = effectList.size 142 143 override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { 144 super.onAttachedToRecyclerView(recyclerView) 145 mRecyclerView = recyclerView 146 itemTouchHelper.attachToRecyclerView(mRecyclerView) 147 } 148 149 private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback( 150 ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT 151 ) { 152 153 var dragFrom = -1 154 var dragTo = -1 155 override fun onMove( 156 recyclerView: RecyclerView, 157 viewHolder: RecyclerView.ViewHolder, 158 target: RecyclerView.ViewHolder 159 ): Boolean { 160 161 val fromPos = viewHolder.adapterPosition 162 val toPos = target.adapterPosition 163 if (dragFrom == -1) { 164 dragFrom = fromPos 165 } 166 dragTo = toPos 167 effectList.add(toPos, effectList.removeAt(fromPos)) 168 recyclerView.adapter?.notifyItemMoved(fromPos, toPos) 169 return true 170 } 171 172 override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 173 val position = viewHolder.adapterPosition 174 effectList.removeAt(position) 175 mRecyclerView.adapter?.notifyItemRemoved(position) 176 NativeInterface.removeEffectAt(position) 177 for (i in position until effectList.size) { 178 var holder = mRecyclerView.findViewHolderForAdapterPosition(i) as EffectsHolder 179 holder.bindEffect(holder.effect, i) 180 } 181 } 182 183 override fun clearView( 184 recyclerView: RecyclerView, 185 viewHolder: RecyclerView.ViewHolder 186 ) { 187 super.clearView(recyclerView, viewHolder) 188 if (dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) { 189 rotateItems(dragFrom, dragTo) 190 } 191 dragFrom = -1 192 dragTo = -1 193 } 194 195 override fun isLongPressDragEnabled(): Boolean = false 196 override fun isItemViewSwipeEnabled(): Boolean = false 197 198 fun rotateItems(fromPos: Int, toPos: Int) { 199 val a = min(fromPos, toPos) 200 val b = max(fromPos, toPos) 201 for (i in a..b) { 202 var holder = mRecyclerView.findViewHolderForAdapterPosition(i) as EffectsHolder 203 holder.bindEffect(holder.effect, i) 204 } 205 NativeInterface.rotateEffectAt(fromPos, toPos) 206 } 207 }) 208 209 } 210