Skip to content
This repository has been archived by the owner on Apr 26, 2020. It is now read-only.

Commit

Permalink
Fixed inconsistent selection state after activity recreation
Browse files Browse the repository at this point in the history
  • Loading branch information
DreierF committed Jan 12, 2018
1 parent 43e802b commit c2152ea
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,25 @@
*/
package de.dreier.mytargets.base.fragments

import android.os.Bundle
import android.support.annotation.PluralsRes
import android.support.design.widget.Snackbar
import android.view.View
import com.evernote.android.state.State
import com.google.firebase.analytics.FirebaseAnalytics
import de.dreier.mytargets.R
import de.dreier.mytargets.base.adapters.ListAdapterBase
import de.dreier.mytargets.shared.models.IIdSettable
import de.dreier.mytargets.shared.models.IRecursiveModel
import de.dreier.mytargets.utils.MultiSelectorBundler
import de.dreier.mytargets.utils.multiselector.MultiSelector
import de.dreier.mytargets.utils.multiselector.OnItemLongClickListener
import de.dreier.mytargets.utils.multiselector.SelectableViewHolder
import timber.log.Timber

/**
* @param <T> Model of the item which is managed within the fragment.
</T> */
abstract class EditableListFragmentBase<T, U : ListAdapterBase<*, T>> : ListFragmentBase<T, U>(), OnItemLongClickListener<T> where T : IIdSettable, T : IRecursiveModel {

@State(MultiSelectorBundler::class)
var selector = MultiSelector()

/**
Expand All @@ -44,6 +43,18 @@ abstract class EditableListFragmentBase<T, U : ListAdapterBase<*, T>> : ListFrag

var actionModeCallback: ItemActionModeCallback? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Timber.d("onCreate: %b", selector.selectable)

if(savedInstanceState != null) {
selector.restoreSelectionStates(savedInstanceState.getBundle(KEY_SELECTOR)!!)
if(selector.selectable) {
actionModeCallback?.restartActionMode()
}
}
}

override fun onResume() {
super.onResume()
reloadData()
Expand All @@ -52,14 +63,19 @@ abstract class EditableListFragmentBase<T, U : ListAdapterBase<*, T>> : ListFrag
fun onDelete(deletedIds: List<Long>) {
FirebaseAnalytics.getInstance(context!!).logEvent("delete", null)
val deleted = deleteItems(deletedIds)
val message = resources
.getQuantityString(itemTypeDelRes, deleted.size, deleted.size)
val message = resources.getQuantityString(itemTypeDelRes, deleted.size, deleted.size)
val coordinatorLayout = view!!.findViewById<View>(R.id.coordinatorLayout)
Snackbar.make(coordinatorLayout, message, Snackbar.LENGTH_LONG)
.setAction(R.string.undo) { undoDeletion(deleted) }
.show()
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBundle(KEY_SELECTOR, selector.saveSelectionStates())

}

private fun deleteItems(deletedIds: List<Long>): MutableList<T> {
val deleted = deletedIds
.map { id -> adapter!!.getItemById(id) }
Expand All @@ -85,8 +101,11 @@ abstract class EditableListFragmentBase<T, U : ListAdapterBase<*, T>> : ListFrag
}

override fun onClick(holder: SelectableViewHolder<T>, item: T?) {
Timber.d("onClick: ")
if (!actionModeCallback!!.click(holder)) {
Timber.d("item: ")
if (item != null) {
Timber.d("onSelected: ")
onSelected(item)
}
}
Expand All @@ -105,6 +124,6 @@ abstract class EditableListFragmentBase<T, U : ListAdapterBase<*, T>> : ListFrag

companion object {
const val ITEM_ID = "id"
const val KEY_SELECTOR = "selector"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import android.view.MenuItem
import de.dreier.mytargets.R
import de.dreier.mytargets.utils.multiselector.MultiSelector
import de.dreier.mytargets.utils.multiselector.SelectableViewHolder
import timber.log.Timber


typealias EditCallback = (Long) -> Unit
Expand All @@ -31,13 +32,11 @@ typealias DeleteCallback = (List<Long>) -> Unit

typealias StatisticsCallback = (List<Long>) -> Unit

class ItemActionModeCallback(private val fragment: ListFragmentBase<*, *>,
private val selector: MultiSelector,
/**
* Resource used to set title when items are selected.
*/
@PluralsRes
private val itemTypeSelRes: Int) : ActionMode.Callback {
class ItemActionModeCallback(
private val fragment: ListFragmentBase<*, *>,
private val selector: MultiSelector,
@PluralsRes private val itemTitleRes: Int
) : ActionMode.Callback {

private var actionMode: ActionMode? = null

Expand All @@ -51,17 +50,17 @@ class ItemActionModeCallback(private val fragment: ListFragmentBase<*, *>,

override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
val edit = menu.findItem(R.id.action_edit)
edit.isVisible = selector.selectedIds.size == 1
edit.isVisible = selector.selectedItemCount == 1
menu.findItem(R.id.action_statistics).isVisible = statisticsCallback != null
menu.findItem(R.id.action_delete).isVisible = deleteCallback != null
return false
}

override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
selector.setSelectable(true)
Timber.d("onCreateActionMode")
selector.selectable = true
actionMode = mode
val inflater = mode.menuInflater
inflater.inflate(R.menu.context_menu_edit_delete, menu)
mode.menuInflater.inflate(R.menu.context_menu_edit_delete, menu)
return true
}

Expand All @@ -88,25 +87,35 @@ class ItemActionModeCallback(private val fragment: ListFragmentBase<*, *>,
}

override fun onDestroyActionMode(mode: ActionMode) {
selector.setSelectable(false)
Timber.d("onDestroyActionMode")
selector.selectable = false
selector.clearSelections()
actionMode = null
}

fun longClick(holder: SelectableViewHolder<*>) {
Timber.d("longClick")
if (actionMode == null) {
Timber.d("startActionMode")
val activity = fragment.getActivity() as AppCompatActivity?
activity!!.startSupportActionMode(this)
selector.setSelectable(true)
}
selector.setSelected(holder, true)
updateTitle()
}

fun restartActionMode() {
Timber.d("restartActionMode")
val activity = fragment.getActivity() as AppCompatActivity?
activity!!.startSupportActionMode(this)
updateTitle()
}

/**
* Returns true if the click has been handled.
*/
fun click(holder: SelectableViewHolder<*>): Boolean {
Timber.d("IAMC#click")
if (selector.tapSelection(holder)) {
updateTitle()
return true
Expand All @@ -115,16 +124,15 @@ class ItemActionModeCallback(private val fragment: ListFragmentBase<*, *>,
}

private fun updateTitle() {
Timber.d("updateTitle")
if (actionMode == null) {
return
}
val count = selector.selectedIds.size
val count = selector.selectedItemCount
if (count == 0) {
actionMode!!.finish()
finish()
} else {
val title = fragment.getResources()
.getQuantityString(itemTypeSelRes, count, count)
actionMode!!.title = title
actionMode!!.title = fragment.getResources().getQuantityString(itemTitleRes, count, count)
actionMode!!.invalidate()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ abstract class SelectItemFragmentBase<T, U : ListAdapterBase<out ItemBindingHold
*/
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
selector.setSelectable(true)
selector.selectable = true
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ class RoundFragment : EditableListFragment<End>() {

init {
itemTypeDelRes = R.plurals.passe_deleted
actionModeCallback = ItemActionModeCallback(this, selector,
R.plurals.passe_selected)
actionModeCallback = ItemActionModeCallback(this, selector, R.plurals.passe_selected)
actionModeCallback?.setEditCallback(this::onEdit)
actionModeCallback?.setDeleteCallback(this::onDelete)
}
Expand Down

This file was deleted.

12 changes: 3 additions & 9 deletions app/src/main/java/de/dreier/mytargets/utils/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ object Utils {
get() = VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP

fun getMonthHeader(context: Context, date: LocalDate): Header {
val dateFormat = DateTimeFormatter.ofPattern("MMMM yyyy",
getCurrentLocale(context))
val dateFormat = DateTimeFormatter.ofPattern("MMMM yyyy", getCurrentLocale(context))
val month = getMonthStart(date)
return Header(month.toEpochDay(), month.format(dateFormat))
}
Expand All @@ -68,8 +67,7 @@ object Utils {
// We use an AlarmManager to call this intent in 100ms
val mPendingIntentId = 223344
val mPendingIntent = PendingIntent
.getActivity(context, mPendingIntentId, intent,
PendingIntent.FLAG_CANCEL_CURRENT)
.getActivity(context, mPendingIntentId, intent, PendingIntent.FLAG_CANCEL_CURRENT)
val mgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent)

Expand Down Expand Up @@ -98,9 +96,7 @@ object Utils {
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar

or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar

or View.SYSTEM_UI_FLAG_IMMERSIVE)
}

Expand Down Expand Up @@ -135,16 +131,14 @@ object Utils {
}

fun argb(alpha: Int, color: Int): Int {
return Color.argb(alpha, Color.red(color), Color.green(color),
Color.blue(color))
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color))
}

@Suppress("DEPRECATION")
fun getCurrentLocale(context: Context): Locale {
return if (VERSION.SDK_INT >= VERSION_CODES.N) {
context.resources.configuration.locales.get(0)
} else {

context.resources.configuration.locale
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import java.util.*
class MultiSelector : SelectorBase() {
private val selections = HashSet<Long>()

val selectedItemCount: Int
get() = selections.size

val selectedIds: ArrayList<Long>
get() = ArrayList(selections)

Expand All @@ -43,13 +46,13 @@ class MultiSelector : SelectorBase() {
}

override fun saveSelectionStates(bundle: Bundle) {
bundle.putLongArray(SELECTION_IDS, selectedIds.toLongArray())
bundle.putLongArray(SELECTION_IDS, selections.toLongArray())
}

override fun restoreSelectionStates(savedStates: Bundle) {
super.restoreSelectionStates(savedStates)
val selectedIds = savedStates.getLongArray(SELECTION_IDS)
restoreSelections(selectedIds!!.toList())
restoreSelections(selectedIds.toList())
}

private fun restoreSelections(selected: List<Long>?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ import android.support.annotation.CallSuper

abstract class SelectorBase {
protected var tracker = WeakHolderTracker()
private var isSelectable: Boolean = false

fun setSelectable(isSelectable: Boolean) {
this.isSelectable = isSelectable
refreshAllHolders()
}
var selectable: Boolean = false
set(value) {
field = value
refreshAllHolders()
}

protected fun refreshAllHolders() {
for (holder in tracker.trackedHolders) {
Expand All @@ -38,9 +37,8 @@ abstract class SelectorBase {
if (holder is ItemBindingHolder<*> && holder.item != null) {
holder.bindItem()
}
holder.isSelectable = isSelectable
val isActivated = isSelected(holder.itemIdentifier)
holder.isActivated = isActivated
holder.isSelectable = selectable
holder.isActivated = isSelected(holder.itemIdentifier)
}
}

Expand All @@ -54,7 +52,7 @@ abstract class SelectorBase {

fun tapSelection(holder: SelectableHolder): Boolean {
val itemId = holder.itemIdentifier
return if (isSelectable) {
return if (selectable) {
val isSelected = isSelected(itemId)
setSelected(itemId, !isSelected)
true
Expand All @@ -65,7 +63,7 @@ abstract class SelectorBase {

fun saveSelectionStates(): Bundle {
val bundle = Bundle()
bundle.putBoolean(SELECTIONS_STATE, isSelectable)
bundle.putBoolean(SELECTIONS_STATE, selectable)
saveSelectionStates(bundle)
return bundle
}
Expand All @@ -79,7 +77,7 @@ abstract class SelectorBase {

@CallSuper
open fun restoreSelectionStates(savedStates: Bundle) {
isSelectable = savedStates.getBoolean(SELECTIONS_STATE)
selectable = savedStates.getBoolean(SELECTIONS_STATE)
}

companion object {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

buildscript {
ext.androidPluginVersion = '3.0.1'
ext.kotlinVersion = '1.2.20-eap-33'
ext.kotlinVersion = '1.2.20-eap-71'
repositories {
jcenter()
google()
Expand Down

0 comments on commit c2152ea

Please sign in to comment.