From b1ac36fd16c68d690048f157ea3ab7b30907fb02 Mon Sep 17 00:00:00 2001 From: Andries Smit Date: Mon, 14 Mar 2022 20:46:30 +0100 Subject: [PATCH] feat: automation option menu --- .../general/automation/AutomationEvent.kt | 6 +- .../general/automation/AutomationFragment.kt | 246 ++++++++++++------ .../general/automation/AutomationPlugin.kt | 5 +- .../automation/dialogs/EditEventDialog.kt | 2 +- .../dragHelpers/ItemTouchHelperAdapter.kt | 18 +- .../dragHelpers/ItemTouchHelperViewHolder.kt | 20 -- .../SimpleItemTouchHelperCallback.kt | 80 ++---- .../main/res/layout/automation_event_item.xml | 24 +- .../src/main/res/menu/menu_automation.xml | 16 ++ .../main/res/menu/menu_delete_selection.xml | 11 + automation/src/main/res/values/strings.xml | 7 + .../general/automation/AutomationEventTest.kt | 4 +- 12 files changed, 245 insertions(+), 194 deletions(-) delete mode 100644 automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperViewHolder.kt create mode 100644 automation/src/main/res/menu/menu_automation.xml create mode 100644 automation/src/main/res/menu/menu_delete_selection.xml diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.kt index 77150914c3..13842ec74a 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.kt @@ -20,6 +20,7 @@ class AutomationEvent(private val injector: HasAndroidInjector) { var title: String = "" var isEnabled = true + var position = -1 var systemAction: Boolean = false // true = generated by AAPS, false = entered by user var readOnly: Boolean = false // removing, editing disabled var autoRemove: Boolean = false // auto-remove once used @@ -66,11 +67,12 @@ class AutomationEvent(private val injector: HasAndroidInjector) { .toString() } - fun fromJSON(data: String): AutomationEvent { + fun fromJSON(data: String, position: Int): AutomationEvent { val d = JSONObject(data) title = d.optString("title", "") isEnabled = d.optBoolean("enabled", true) systemAction = d.optBoolean("systemAction", false) + this.position = position readOnly = d.optBoolean("readOnly", false) autoRemove = d.optBoolean("autoRemove", false) userAction = d.optBoolean("userAction", false) @@ -88,4 +90,4 @@ class AutomationEvent(private val injector: HasAndroidInjector) { fun shouldRun(): Boolean { return lastRun <= dateUtil.now() - T.mins(5).msecs() } -} \ No newline at end of file +} diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt index d94bf5b6a1..8543558515 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt @@ -2,13 +2,11 @@ package info.nightscout.androidaps.plugins.general.automation import android.annotation.SuppressLint import android.content.Context -import android.graphics.Color import android.os.Bundle import android.text.method.ScrollingMovementMethod -import android.view.LayoutInflater -import android.view.MotionEvent -import android.view.View -import android.view.ViewGroup +import android.util.SparseArray +import android.view.* +import androidx.core.util.forEach import android.widget.ImageView import android.widget.LinearLayout import androidx.annotation.DrawableRes @@ -25,10 +23,6 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog -import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperAdapter -import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperViewHolder -import info.nightscout.androidaps.plugins.general.automation.dragHelpers.OnStartDragListener -import info.nightscout.androidaps.plugins.general.automation.dragHelpers.SimpleItemTouchHelperCallback import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector @@ -37,6 +31,9 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog import io.reactivex.rxjava3.kotlin.plusAssign import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperAdapter +import info.nightscout.androidaps.plugins.general.automation.dragHelpers.OnStartDragListener +import info.nightscout.androidaps.plugins.general.automation.dragHelpers.SimpleItemTouchHelperCallback import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -55,13 +52,14 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { private var disposable: CompositeDisposable = CompositeDisposable() private lateinit var eventListAdapter: EventListAdapter - - private var itemTouchHelper: ItemTouchHelper? = null + private var selectedItems: SparseArray = SparseArray() + private var removeActionMode: ActionMode? = null + private var sortActionMode: ActionMode? = null + private val itemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback()) private var _binding: AutomationFragmentBinding? = null - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -71,14 +69,15 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) eventListAdapter = EventListAdapter() binding.eventListView.layoutManager = LinearLayoutManager(context) binding.eventListView.adapter = eventListAdapter - binding.logView.movementMethod = ScrollingMovementMethod() - binding.fabAddEvent.setOnClickListener { + removeActionMode?.finish() + sortActionMode?.finish() val dialog = EditEventDialog() val args = Bundle() args.putString("event", AutomationEvent(injector).toJSON()) @@ -87,12 +86,10 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { dialog.show(childFragmentManager, "EditEventDialog") } - val callback: ItemTouchHelper.Callback = SimpleItemTouchHelperCallback(eventListAdapter) - itemTouchHelper = ItemTouchHelper(callback) - itemTouchHelper?.attachToRecyclerView(binding.eventListView) - + itemTouchHelper.attachToRecyclerView(binding.eventListView) } + @SuppressLint("NotifyDataSetChanged") @Synchronized override fun onResume() { super.onResume() @@ -113,6 +110,8 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { @Synchronized override fun onPause() { + removeActionMode?.finish() + sortActionMode?.finish() super.onPause() disposable.clear() } @@ -120,9 +119,39 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.finish() + sortActionMode?.finish() _binding = null } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_automation, menu) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + // Only show when tab automation is shown + menu.findItem(R.id.nav_remove_automation_items)?.isVisible = isResumed + menu.findItem(R.id.nav_sort_automation_items)?.isVisible = isResumed + super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.nav_remove_automation_items -> { + removeActionMode = activity?.startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_sort_automation_items -> { + sortActionMode = activity?.startActionMode(SortActionModeCallback()) + true + } + + else -> false + } + } + @Synchronized private fun updateGui() { if (_binding == null) return @@ -134,7 +163,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { } override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) { - itemTouchHelper?.startDrag(viewHolder) + itemTouchHelper.startDrag(viewHolder) } fun fillIconSet(connector: TriggerConnector, set: HashSet) { @@ -166,20 +195,22 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { @SuppressLint("ClickableViewAccessibility") override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val event = automationPlugin.at(position) - holder.binding.rootLayout.setBackgroundColor(rh.gc( - if (event.userAction) R.color.mdtp_line_dark - else if (event.areActionsValid()) R.color.ribbonDefault - else R.color.errorAlertBackground) + val automation = automationPlugin.at(position) + holder.binding.rootLayout.setBackgroundColor( + rh.gc( + if (automation.userAction) R.color.mdtp_line_dark + else if (automation.areActionsValid()) R.color.ribbonDefault + else R.color.errorAlertBackground + ) ) - holder.binding.eventTitle.text = event.title - holder.binding.enabled.isChecked = event.isEnabled - holder.binding.enabled.isEnabled = !event.readOnly + holder.binding.eventTitle.text = automation.title + holder.binding.enabled.isChecked = automation.isEnabled + holder.binding.enabled.isEnabled = !automation.readOnly holder.binding.iconLayout.removeAllViews() // trigger icons val triggerIcons = HashSet() - if (event.userAction) triggerIcons.add(R.drawable.ic_danar_useropt) - fillIconSet(event.trigger, triggerIcons) + if (automation.userAction) triggerIcons.add(R.drawable.ic_danar_useropt) + fillIconSet(automation.trigger, triggerIcons) for (res in triggerIcons) { addImage(res, holder.context, holder.binding.iconLayout) } @@ -191,7 +222,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { holder.binding.iconLayout.addView(iv) // action icons val actionIcons = HashSet() - for (action in event.actions) { + for (action in automation.actions) { actionIcons.add(action.icon()) } for (res in actionIcons) { @@ -199,70 +230,133 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { } // enabled event holder.binding.enabled.setOnClickListener { - event.isEnabled = holder.binding.enabled.isChecked + automation.isEnabled = holder.binding.enabled.isChecked rxBus.send(EventAutomationDataChanged()) } - // edit event - holder.binding.rootLayout.setOnClickListener { - val dialog = EditEventDialog() - val args = Bundle() - args.putString("event", event.toJSON()) - args.putInt("position", position) - dialog.arguments = args - dialog.show(childFragmentManager, "EditEventDialog") - } - // Start a drag whenever the handle view it touched - holder.binding.iconSort.setOnTouchListener { v: View, motionEvent: MotionEvent -> - if (motionEvent.action == MotionEvent.ACTION_DOWN) { - this@AutomationFragment.onStartDrag(holder) - return@setOnTouchListener true + holder.binding.aapsLogo.visibility = (automation.systemAction).toVisibility() + + fun updateSelection(selected: Boolean) { + if (selected) { + selectedItems.put(position, automation) + } else { + selectedItems.remove(position) } - v.onTouchEvent(motionEvent) + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) } - // remove event - holder.binding.iconTrash.setOnClickListener { - OKDialog.showConfirmation(requireContext(), rh.gs(R.string.removerecord) + " " + automationPlugin.at(position).title, - { - uel.log(Action.AUTOMATION_REMOVED, Sources.Automation, automationPlugin.at(position).title) - automationPlugin.removeAt(position) - notifyItemRemoved(position) - }, { - rxBus.send(EventAutomationUpdateGui()) - }) + + holder.binding.rootLayout.setOnTouchListener { _, touchEvent -> + if (touchEvent.actionMasked == MotionEvent.ACTION_UP && sortActionMode == null && removeActionMode == null) { + val dialog = EditEventDialog() + val args = Bundle() + args.putString("event", automation.toJSON()) + args.putInt("position", position) + dialog.arguments = args + dialog.show(childFragmentManager, "EditEventDialog") + } + if (touchEvent.actionMasked == MotionEvent.ACTION_DOWN && sortActionMode != null) { + onStartDrag(holder) + } + if (touchEvent.actionMasked == MotionEvent.ACTION_UP && removeActionMode != null) { + holder.binding.cbRemove.toggle() + updateSelection(holder.binding.cbRemove.isChecked) + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + return@setOnTouchListener true } - holder.binding.iconTrash.visibility = (!event.readOnly).toVisibility() - holder.binding.aapsLogo.visibility = (event.systemAction).toVisibility() + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> updateSelection(value) } + holder.binding.iconSort.visibility = (sortActionMode != null).toVisibility() + holder.binding.cbRemove.visibility = (removeActionMode != null).toVisibility() + holder.binding.cbRemove.isEnabled = !automation.readOnly + holder.binding.enabled.visibility = if (removeActionMode == null) View.VISIBLE else View.INVISIBLE } override fun getItemCount(): Int = automationPlugin.size() override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { automationPlugin.swap(fromPosition, toPosition) - notifyItemMoved(fromPosition, toPosition) return true } - override fun onItemDismiss(position: Int) { - activity?.let { activity -> - OKDialog.showConfirmation( - activity, - rh.gs(R.string.removerecord) + " " + automationPlugin.at(position).title, - { - uel.log(Action.AUTOMATION_REMOVED, Sources.Automation, automationPlugin.at(position).title) - automationPlugin.removeAt(position) - notifyItemRemoved(position) - rxBus.send(EventAutomationDataChanged()) - }, { rxBus.send(EventAutomationUpdateGui()) }) + override fun onDrop() { + rxBus.send(EventAutomationDataChanged()) + } + + inner class ViewHolder(view: View, val context: Context) : RecyclerView.ViewHolder(view) { + + val binding = AutomationEventItemBinding.bind(view) + } + } + + inner class SortActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.title = rh.gs(R.string.sort_label) + binding.eventListView.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem) = false + + override fun onDestroyActionMode(mode: ActionMode?) { + sortActionMode = null + binding.eventListView.adapter?.notifyDataSetChanged() + } + } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.eventListView.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true + } + + else -> false } } - inner class ViewHolder(view: View, val context: Context) : RecyclerView.ViewHolder(view), ItemTouchHelperViewHolder { + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.eventListView.adapter?.notifyDataSetChanged() + } + } - val binding = AutomationEventItemBinding.bind(view) + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val event = selectedItems.valueAt(0) + return rh.gs(R.string.removerecord) + " " + event.title + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } - override fun onItemSelected() = itemView.setBackgroundColor(Color.LTGRAY) - - override fun onItemClear() = itemView.setBackgroundColor(rh.gc(R.color.ribbonDefault)) + private fun removeSelected() { + if (selectedItems.size() > 0) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, event -> + uel.log(Action.AUTOMATION_REMOVED, Sources.Automation, event.title) + automationPlugin.removeAt(event.position) + rxBus.send(EventAutomationDataChanged()) + } + removeActionMode?.finish() + }) + } + } else { + removeActionMode?.finish() } } } diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt index 8690a68f0e..4fda2b539f 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt @@ -169,14 +169,14 @@ class AutomationPlugin @Inject constructor( val array = JSONArray(data) for (i in 0 until array.length()) { val o = array.getJSONObject(i) - val event = AutomationEvent(injector).fromJSON(o.toString()) + val event = AutomationEvent(injector).fromJSON(o.toString(), i) automationEvents.add(event) } } catch (e: JSONException) { e.printStackTrace() } else - automationEvents.add(AutomationEvent(injector).fromJSON(event)) + automationEvents.add(AutomationEvent(injector).fromJSON(event, 0)) } @Synchronized @@ -305,7 +305,6 @@ class AutomationPlugin @Inject constructor( @Synchronized fun swap(fromPosition: Int, toPosition: Int) { Collections.swap(automationEvents, fromPosition, toPosition) - rxBus.send(EventAutomationDataChanged()) } @Synchronized diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt index 94c87f75bd..783b9dace3 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt @@ -56,7 +56,7 @@ class EditEventDialog : DialogFragmentWithDate() { // load data from bundle (savedInstanceState ?: arguments)?.let { bundle -> position = bundle.getInt("position", -1) - bundle.getString("event")?.let { event = AutomationEvent(injector).fromJSON(it) } + bundle.getString("event")?.let { event = AutomationEvent(injector).fromJSON(it, position) } } onCreateViewGeneral() diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperAdapter.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperAdapter.kt index bdd68080ef..e873e73173 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperAdapter.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperAdapter.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.general.automation.dragHelpers - interface ItemTouchHelperAdapter { + /** * Called when an item has been dragged far enough to trigger a move. This is called every time * an item is shifted, and **not** at the end of a "drop" event.

@@ -18,16 +18,6 @@ interface ItemTouchHelperAdapter { */ fun onItemMove(fromPosition: Int, toPosition: Int): Boolean - /** - * Called when an item has been dismissed by a swipe.

- *

- * Implementations should call [RecyclerView.Adapter.notifyItemRemoved] after - * adjusting the underlying data to reflect this removal. - * - * @param position The position of the item dismissed. - * - * @see RecyclerView.getAdapterPositionFor - * @see RecyclerView.ViewHolder.getAdapterPosition - */ - fun onItemDismiss(position: Int) -} \ No newline at end of file + fun onDrop() + +} diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperViewHolder.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperViewHolder.kt deleted file mode 100644 index a873e5253d..0000000000 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperViewHolder.kt +++ /dev/null @@ -1,20 +0,0 @@ -package info.nightscout.androidaps.plugins.general.automation.dragHelpers - -/** - * Interface to notify an item ViewHolder of relevant callbacks from [ ]. - * - * @author Paul Burke (ipaulpro) - */ -interface ItemTouchHelperViewHolder { - /** - * Called when the [ItemTouchHelper] first registers an item as being moved or swiped. - * Implementations should update the item view to indicate it's active state. - */ - fun onItemSelected() - - /** - * Called when the [ItemTouchHelper] has completed the move or swipe, and the active item - * state should be cleared. - */ - fun onItemClear() -} \ No newline at end of file diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/SimpleItemTouchHelperCallback.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/SimpleItemTouchHelperCallback.kt index 1e633d4627..76d15f5477 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/SimpleItemTouchHelperCallback.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/SimpleItemTouchHelperCallback.kt @@ -1,85 +1,37 @@ package info.nightscout.androidaps.plugins.general.automation.dragHelpers -import android.graphics.Canvas -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import kotlin.math.abs +import info.nightscout.androidaps.plugins.general.automation.AutomationFragment +class SimpleItemTouchHelperCallback : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.START or ItemTouchHelper.END, 0) { -/** - * An implementation of [ItemTouchHelper.Callback] that enables basic drag & drop and - * swipe-to-dismiss. Drag events are automatically started by an item long-press.

- * - * Expects the `RecyclerView.Adapter` to listen for [ ] callbacks and the `RecyclerView.ViewHolder` to implement - * [ItemTouchHelperViewHolder]. - * - */ -class SimpleItemTouchHelperCallback(private val mAdapter: ItemTouchHelperAdapter) : ItemTouchHelper.Callback() { override fun isLongPressDragEnabled(): Boolean { - return true - } - - override fun isItemViewSwipeEnabled(): Boolean { return false } - override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { // Set movement flags based on the layout manager - return if (recyclerView.layoutManager is GridLayoutManager) { - val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT - val swipeFlags = 0 - makeMovementFlags(dragFlags, swipeFlags) - } else { - val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN - val swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END - makeMovementFlags(dragFlags, swipeFlags) - } - } + override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + val adapter = recyclerView.adapter as AutomationFragment.EventListAdapter + val from = viewHolder.layoutPosition + val to = target.layoutPosition + adapter.onItemMove(from, to) + adapter.notifyItemMoved(from, to) - override fun onMove(recyclerView: RecyclerView, source: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { - if (source.itemViewType != target.itemViewType) { - return false - } - // Notify the adapter of the move - mAdapter.onItemMove(source.absoluteAdapterPosition, target.absoluteAdapterPosition) return true } - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, i: Int) { // Notify the adapter of the dismissal - mAdapter.onItemDismiss(viewHolder.absoluteAdapterPosition) - } + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} - override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) { - if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { // Fade out the view as it is swiped out of the parent's bounds - val alpha = ALPHA_FULL - abs(dX) / viewHolder.itemView.width.toFloat() - viewHolder.itemView.alpha = alpha - viewHolder.itemView.translationX = dX - } else { - super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) - } - } - - override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { // We only want the active item to change - if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { - if (viewHolder is ItemTouchHelperViewHolder) { // Let the view holder know that this item is being moved or dragged - val itemViewHolder: ItemTouchHelperViewHolder = viewHolder - itemViewHolder.onItemSelected() - } - } + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { super.onSelectedChanged(viewHolder, actionState) + if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { + viewHolder?.itemView?.alpha = 0.5f + } } override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { super.clearView(recyclerView, viewHolder) - viewHolder.itemView.alpha = ALPHA_FULL - if (viewHolder is ItemTouchHelperViewHolder) { // Tell the view holder it's time to restore the idle state - val itemViewHolder: ItemTouchHelperViewHolder = viewHolder - itemViewHolder.onItemClear() - } + viewHolder.itemView.alpha = 1.0f + (recyclerView.adapter as AutomationFragment.EventListAdapter).onDrop() } - - companion object { - const val ALPHA_FULL = 1.0f - } - -} \ No newline at end of file +} diff --git a/automation/src/main/res/layout/automation_event_item.xml b/automation/src/main/res/layout/automation_event_item.xml index 04402e6a38..b91efdbb2c 100644 --- a/automation/src/main/res/layout/automation_event_item.xml +++ b/automation/src/main/res/layout/automation_event_item.xml @@ -1,6 +1,7 @@ + app:layout_constraintTop_toTopOf="parent" + tools:text="Event title" /> - @@ -78,10 +78,10 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" android:orientation="horizontal" + android:paddingBottom="4dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/iconSort" /> + app:layout_constraintTop_toBottomOf="@+id/enabled" /> - - \ No newline at end of file + diff --git a/automation/src/main/res/menu/menu_automation.xml b/automation/src/main/res/menu/menu_automation.xml new file mode 100644 index 0000000000..3f51834c0a --- /dev/null +++ b/automation/src/main/res/menu/menu_automation.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/automation/src/main/res/menu/menu_delete_selection.xml b/automation/src/main/res/menu/menu_delete_selection.xml new file mode 100644 index 0000000000..1090412c5a --- /dev/null +++ b/automation/src/main/res/menu/menu_delete_selection.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/automation/src/main/res/values/strings.xml b/automation/src/main/res/values/strings.xml index cf546122e2..2e0bb7de7d 100644 --- a/automation/src/main/res/values/strings.xml +++ b/automation/src/main/res/values/strings.xml @@ -116,5 +116,12 @@ Reorder automation_settings User action + Remove automation + Sort automation + Remove selected items + %1$d selected + Are you sure you want to remove %1$d items + Sort + System automation \ No newline at end of file diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt index 30b8fe078e..5b7b42b2f9 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt @@ -54,7 +54,7 @@ class AutomationEventTest : TestBase() { Assert.assertEquals(eventJsonExpected, event.toJSON()) // clone - val clone = AutomationEvent(injector).fromJSON(eventJsonExpected) + val clone = AutomationEvent(injector).fromJSON(eventJsonExpected, 1) // check title Assert.assertEquals(event.title, clone.title) @@ -70,4 +70,4 @@ class AutomationEventTest : TestBase() { Assert.assertFalse(event.actions === clone.actions) // not the same object reference Assert.assertEquals(clone.toJSON(), clone.toJSON()) } -} \ No newline at end of file +}