Skip to content

Instantly share code, notes, and snippets.

@oligazar
Last active January 17, 2018 09:34
Show Gist options
  • Save oligazar/021e64f181c8e56605a669ce06534498 to your computer and use it in GitHub Desktop.
Save oligazar/021e64f181c8e56605a669ce06534498 to your computer and use it in GitHub Desktop.
private var listener: ChildEventListener? = null
override fun onStart() {
super.onStart()
Log.d("OffersFragment", "onStart\n")
listener = mDbRef.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(snap: DataSnapshot, previousName: String?) {
snap.getValue(Category::class.java)?.let { category ->
category.key = snap.key
Log.d("OffersFragment", "onChildAdded, category: $category")
if (categories.contains(category)) {
val index = categories.indexOf(category)
categories[index] = category
mAdapter.notifyItemChanged(index)
return
}
val index = categories.indices
.firstOrNull { categories[it].key == previousName }
?.let { it + 1 } ?: 0
categories.add(index, category)
mAdapter.notifyItemInserted(index)
}
}
override fun onCancelled(p0: DatabaseError?) { }
override fun onChildMoved(snap: DataSnapshot?, previousName: String?) {
snap?.getValue(Category::class.java)?.let { category ->
Log.d("OffersFragment", "onChildMoved")
categories.indices.forEach { Log.d("OffersFragment", "onChildMoved, categories[$it]: ${categories[it]}") }
Log.d("OffersFragment", "onChildMoved")
category.key = snap.key
Log.d("OffersFragment", "onChildMoved, category: $category")
val prevPos = categories.indexOf(category)
Log.d("OffersFragment", "onChildMoved, oldSort: ${categories[prevPos].sort}, newSort: ${category.sort}")
if (categories[prevPos].sort == category.sort) {
Log.d("OffersFragment", "onChildMoved, return")
return
}
val newPos = if (previousName == null) 0
else categories
.indexOf(categories.first { it.key == previousName } )
.let { newPos -> if (newPos > prevPos) newPos else newPos + 1 }
Collections.swap(categories, prevPos, newPos)
val catA = categories[prevPos]
val catB = categories[newPos]
val temp = catA.sort
catA.sort = catB.sort
catB.sort = temp
mAdapter.notifyItemMoved(prevPos, newPos)
Log.d("OffersFragment", "onChildMoved, key: ${snap.key}, previous: $previousName")
Log.d("OffersFragment", "onChildMoved, previousPos: $prevPos, newPos: $newPos")
Log.d("OffersFragment", "onChildMoved, catA: ${categories[newPos]}")
Log.d("OffersFragment", "onChildMoved, catB: ${categories[prevPos]}")
}
}
override fun onChildChanged(snap: DataSnapshot, p1: String?) {
Log.d("OffersFragment", "onChildChanged")
snap.getValue(Category::class.java)?.let { category ->
category.key = snap.key
Log.d("OffersFragment", "onChildChanged, category: $category")
if (isDragging) return
if (categories.contains(category)) {
val position = categories.indexOf(category)
categories[position] = category
mAdapter.notifyItemChanged(position)
}
}
}
override fun onChildRemoved(snap: DataSnapshot?) {
snap?.getValue(Category::class.java)?.let { category ->
Log.d("OffersFragment", "onChildRemoved, category: $category")
val position = categories.indexOf(category)
categories.removeAt(position)
mAdapter.notifyItemRemoved(position)
}
}
})
}
override fun onStop() {
super.onStop()
mDbRef.removeEventListener(listener)
}
class OffersCategoriesAdapter(private val dragListener: OnStartDragListener,
private val categories: ArrayList<Category>,
private var user: User?,
private val editHandler: (Category) -> Unit,
private val clickHandler: (Category) -> Unit)
: RecyclerView.Adapter<CategoryVH>(), ItemTouchHelperAdapter, OnUserChangedListener {
private val mDb = FirebaseDatabase.getInstance()
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
Log.d("OffersCategoriesFrag", "adapter.onItemMove from: $fromPosition, to: $toPosition")
Collections.swap(categories, fromPosition, toPosition)
swapSortPosition(categories, fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
return true
}
private fun swapSortPosition(categories: ArrayList<Category>, indA: Int, indB: Int) {
val catA = categories[indA]
val catB = categories[indB]
val temp = catA.sort
catA.sort = catB.sort
catB.sort = temp
val updates = mapOf(
Pair("categories/offers/${catA.key}/sort", catA.sort),
Pair("categories/offers/${catB.key}/sort", catB.sort))
mDb.reference.updateChildren(updates)
Log.d("OffersCategoriesFrag", "adapter.swapSortPosition, catA: $catA, catB: $catB")
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryVH {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_category, parent, false)
return CategoryVH(view)
}
override fun onBindViewHolder(holder: CategoryVH, position: Int) {
val category = categories[position]
Glide.with(holder.itemView.context)
.load(category.picPath)
.apply(RequestOptions()
.centerCrop()
/*.placeholder(R.drawable.ic_remove)*/)
.transition(DrawableTransitionOptions.withCrossFade())
.into(holder.ivPicture)
holder.tvName.text = category.name
holder.btnEdit.setOnClickListener { editHandler(category) }
holder.itemView.setOnClickListener { clickHandler(category) }
holder.dragHandle.setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
dragListener.onStartDrag(holder)
}
}
false
}
setupUser(holder, user)
}
override fun onUserChanged(user: User) {
this.user = user
notifyDataSetChanged()
}
private fun setupUser(holder: CategoryVH, user: User?) {
when (user?.role) {
UserRole.Partner.toString() -> {
holder.btnEdit.visibility = View.GONE
holder.dragHandle.visibility = View.GONE
}
UserRole.Admin.toString() -> {
holder.btnEdit.visibility = View.VISIBLE
holder.dragHandle.visibility = View.VISIBLE
}
else -> {
holder.btnEdit.visibility = View.GONE
holder.dragHandle.visibility = View.GONE
}
}
}
override fun getItemCount() = categories.size
}
class CategoryVH(view: View) : RecyclerView.ViewHolder(view), ItemTouchHelperViewHolder {
val ivPicture = view.findViewById<View>(R.id.ivPicture) as ImageView
val btnEdit = view.findViewById<View>(R.id.btnEdit) as ImageButton
val tvName = view.findViewById<View>(R.id.tvName) as TextView
val dragHandle = view.findViewById<View>(R.id.dragHandle) as ImageView
override fun onItemSelected() {
ivPicture.run {
setPadding(dp(-4))
}
}
override fun onItemClear() {
ivPicture.run {
setPadding(0)
}
}
}
class OffersCategoriesFragment : Fragment(), OnStartDragListener, OnUserChangedListener {
private val _tag = OffersCategoriesFragment::class.java.simpleName
private val categories = ArrayList<Category>()
private lateinit var mAdapter: OffersCategoriesAdapter
private lateinit var mTouchHelper: ItemTouchHelper
private val createCategoryCode = 123
private var fab: FloatingActionButton? = null
private val categoriesRef = FirebaseDatabase
.getInstance()
.getReference("categories")
.child("offers")
.orderByChild("sort")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_offers, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val user = activity?.getPreferences()?.getUser()
mAdapter = OffersCategoriesAdapter(this, categories, user, { category ->
val intent = CreateCategoryActivity.updateCategoryIntent(activity, category.key)
startActivityForResult(intent, createCategoryCode)
}, { category ->
OffersMasterActivity.startMasterActivity(this, category.key)
})
recycler.apply {
adapter = mAdapter
layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
setHasFixedSize(true)
val spacing = resources.getDimension(R.dimen.spacing_small).toInt()
addItemDecoration(VerticalSpaceItemDecoration(spacing, isDip = false))
enableDragging()
}
fab = activity?.findViewById<View>(R.id.fab) as? FloatingActionButton
activity?.getPreferences()?.getUser()?.let {
Log.d(_tag, "onViewCreated, user: $it")
setupUser(it)
}
}
override fun onUserChanged(user: User) {
setupUser(user)
mAdapter.onUserChanged(user)
}
private fun setupUser(user: User) {
when (user.role) {
UserRole.User.toString() -> {
fab?.visibility = View.GONE
}
UserRole.Partner.toString() -> {
fab?.visibility = View.GONE
}
UserRole.Admin.toString() -> {
fab?.visibility = View.VISIBLE
fab?.setOnClickListener {
val newSort = if (categories.isEmpty()) .0 else categories.last().sort + 1
val intent = CreateCategoryActivity.createCategoryIntent(activity, newSort)
startActivityForResult(intent, createCategoryCode)
}
}
}
}
/** OnStartDragListener callback. Notifies when recycler's item started dragging by handle */
override fun onStartDrag(holder: RecyclerView.ViewHolder) {
mTouchHelper.startDrag(holder)
}
private fun RecyclerView.enableDragging() {
val callback = MyTouchHelperCallback(mAdapter)
mTouchHelper = ItemTouchHelper(callback)
mTouchHelper.attachToRecyclerView(this)
}
/** makes sure categories date is cleared just before new data comes from firebase */
private var mClearCategories = false
private fun clearAllIfNeeded() {
if (mClearCategories) {
val size = categories.count()
categories.clear()
mAdapter.notifyItemRangeRemoved(0, size)
mClearCategories = false
}
}
private var listener: ChildEventListener? = null
override fun onStart() {
super.onStart()
Log.d("OffersCategoriesFrag", "onStart\n")
listener = categoriesRef.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(snap: DataSnapshot, previousName: String?) {
snap.getValue(Category::class.java)?.let { category ->
clearAllIfNeeded()
category.key = snap.key
Log.d("OffersCategoriesFrag", "onChildAdded, category: $category")
val index = categories.indices
.firstOrNull { categories[it].key == previousName }
?.let { it + 1 } ?: 0
categories.add(index, category)
mAdapter.notifyItemInserted(index)
}
}
override fun onCancelled(p0: DatabaseError?) { }
override fun onChildMoved(snap: DataSnapshot?, previousName: String?) {
snap?.getValue(Category::class.java)?.let { category ->
Log.d("OffersCategoriesFrag", "onChildMoved")
categories.indices.forEach { Log.d("OffersCategoriesFrag", "onChildMoved, categories[$it]: ${categories[it]}") }
Log.d("OffersCategoriesFrag", "onChildMoved")
category.key = snap.key
Log.d("OffersCategoriesFrag", "onChildMoved, category: $category")
val prevPos = categories.indexOf(category)
Log.d("OffersCategoriesFrag", "onChildMoved, oldSort: ${categories[prevPos].sort}, newSort: ${category.sort}")
if (categories[prevPos].sort == category.sort) {
Log.d("OffersCategoriesFrag", "onChildMoved, return")
return
}
val newPos = if (previousName == null) 0
else categories
.indexOf(categories.first { it.key == previousName } )
.let { newPos -> if (newPos > prevPos) newPos else newPos + 1 }
Collections.swap(categories, prevPos, newPos)
val catA = categories[prevPos]
val catB = categories[newPos]
val temp = catA.sort
catA.sort = catB.sort
catB.sort = temp
mAdapter.notifyItemMoved(prevPos, newPos)
Log.d("OffersCategoriesFrag", "onChildMoved, key: ${snap.key}, previous: $previousName")
Log.d("OffersCategoriesFrag", "onChildMoved, previousPos: $prevPos, newPos: $newPos")
Log.d("OffersCategoriesFrag", "onChildMoved, catA: ${categories[newPos]}")
Log.d("OffersCategoriesFrag", "onChildMoved, catB: ${categories[prevPos]}")
}
}
override fun onChildChanged(snap: DataSnapshot, p1: String?) {
Log.d("OffersCategoriesFrag", "onChildChanged")
snap.getValue(Category::class.java)?.let { category ->
category.key = snap.key
Log.d("OffersCategoriesFrag", "onChildChanged, category: $category")
if (isDragging) return
if (categories.contains(category)) {
val position = categories.indexOf(category)
categories[position] = category
mAdapter.notifyItemChanged(position)
}
}
}
override fun onChildRemoved(snap: DataSnapshot?) {
snap?.getValue(Category::class.java)?.let { category ->
category.key = snap.key
Log.d("OffersCategoriesFrag", "onChildRemoved, category: $category")
val position = categories.indexOf(category)
categories.removeAt(position)
mAdapter.notifyItemRemoved(position)
}
}
})
}
override fun onStop() {
super.onStop()
Log.d("OffersCategoriesFrag", "onStop")
mClearCategories = true
categoriesRef.removeEventListener(listener)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == createCategoryCode) {
if (resultCode == RESULT_OK) {
val message = when (data?.extras?.getString(CreateCategoryActivity.ACTION_EXTRA_KEY)) {
"delete" -> "Категория успешно удалена"
else -> {
"Категория успешно сохранена"
}
}
Snackbar.make(root, message, Snackbar.LENGTH_SHORT).show()
}
}
}
/** restrict childEventListener from affecting adapter when firebase data updates */
private var isDragging = false
inner class MyTouchHelperCallback(private val mAdapter: OffersCategoriesAdapter): ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
return makeMovementFlags(dragFlags, 0)
}
override fun onMove(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
mAdapter.onItemMove(viewHolder.adapterPosition, target.adapterPosition)
return true
}
override fun isLongPressDragEnabled() = false
override fun onSwiped(viewHolder: RecyclerView.ViewHolder?, direction: Int) { }
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
// We only want the active item
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
(viewHolder as? ItemTouchHelperViewHolder)?.onItemSelected()
isDragging = true
}
super.onSelectedChanged(viewHolder, actionState)
}
override fun clearView(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
(viewHolder as? ItemTouchHelperViewHolder)?.onItemClear()
isDragging = false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment