xref: /aosp_15_r20/external/oboe/apps/fxlab/app/src/main/kotlin/com/mobileer/androidfxlab/EffectsAdapter.kt (revision 05767d913155b055644481607e6fa1e35e2fe72c)
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                     ((effectList[index].paramValues[counter] - param.minValue) * 100 / (param.maxValue
96                             - param.minValue)).toInt()
97                 curLabelView.text = floatFormat.format(effectList[index].paramValues[counter])
98                 // Bind param listeners to effects
99                 seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
100                     val paramInd = counter
101                     var timer : Timer? = null
102 
103                     override fun onStartTrackingTouch(p0: SeekBar?) {}
104 
105                     override fun onStopTrackingTouch(seekbar: SeekBar?) {}
106 
107                     override fun onProgressChanged(
108                         seekBar: SeekBar?, progress: Int, fromUser: Boolean
109                     ) {
110                         val fracprogress =
111                             ((seekBar!!.progress / 100f) * (param.maxValue - param.minValue) + param.minValue)
112                         curLabelView.text = floatFormat.format(fracprogress)
113 
114                         timer?.cancel()
115                         timer = Timer()
116                         timer?.schedule(timerTask { updateEffectParam(fracprogress) }, 100)
117                     }
118 
119                     fun updateEffectParam(fracprogress : Float){
120                         effectList[index].paramValues[paramInd] = fracprogress
121                         NativeInterface.updateParamsAt(effect, index)
122                     }
123                 })
124             }
125         }
126 
127     }
128 
129     override fun onCreateViewHolder(
130         parent: ViewGroup,
131         viewType: Int
132     ): EffectsHolder {
133         val myView = LayoutInflater.from(parent.context)
134             .inflate(R.layout.effect_view, parent, false)
135         return EffectsHolder(myView as ViewGroup)
136     }
137 
138     override fun onBindViewHolder(holder: EffectsHolder, position: Int) {
139         holder.bindEffect(effectList[position], position)
140     }
141 
142     override fun getItemCount() = effectList.size
143 
144     override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
145         super.onAttachedToRecyclerView(recyclerView)
146         mRecyclerView = recyclerView
147         itemTouchHelper.attachToRecyclerView(mRecyclerView)
148     }
149 
150     private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
151         ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT
152     ) {
153 
154         var dragFrom = -1
155         var dragTo = -1
156         override fun onMove(
157             recyclerView: RecyclerView,
158             viewHolder: RecyclerView.ViewHolder,
159             target: RecyclerView.ViewHolder
160         ): Boolean {
161 
162             val fromPos = viewHolder.adapterPosition
163             val toPos = target.adapterPosition
164             if (dragFrom == -1) {
165                 dragFrom = fromPos
166             }
167             dragTo = toPos
168             effectList.add(toPos, effectList.removeAt(fromPos))
169             recyclerView.adapter?.notifyItemMoved(fromPos, toPos)
170             return true
171         }
172 
173         override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
174             val position = viewHolder.adapterPosition
175             effectList.removeAt(position)
176             mRecyclerView.adapter?.notifyItemRemoved(position)
177             NativeInterface.removeEffectAt(position)
178             for (i in position until effectList.size) {
179                 var holder = mRecyclerView.findViewHolderForAdapterPosition(i) as EffectsHolder
180                 holder.bindEffect(holder.effect, i)
181             }
182         }
183 
184         override fun clearView(
185             recyclerView: RecyclerView,
186             viewHolder: RecyclerView.ViewHolder
187         ) {
188             super.clearView(recyclerView, viewHolder)
189             if (dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) {
190                 rotateItems(dragFrom, dragTo)
191             }
192             dragFrom = -1
193             dragTo = -1
194         }
195 
196         override fun isLongPressDragEnabled(): Boolean = false
197         override fun isItemViewSwipeEnabled(): Boolean = false
198 
199         fun rotateItems(fromPos: Int, toPos: Int) {
200             val a = min(fromPos, toPos)
201             val b = max(fromPos, toPos)
202             for (i in a..b) {
203                 var holder = mRecyclerView.findViewHolderForAdapterPosition(i) as EffectsHolder
204                 holder.bindEffect(holder.effect, i)
205             }
206             NativeInterface.rotateEffectAt(fromPos, toPos)
207         }
208     })
209 
210 }
211