Skip to content

Instantly share code, notes, and snippets.

@ToBoehmK
Last active March 12, 2022 19:06
Show Gist options
  • Save ToBoehmK/5131aa8545b6240c5c7d8fa372ff4113 to your computer and use it in GitHub Desktop.
Save ToBoehmK/5131aa8545b6240c5c7d8fa372ff4113 to your computer and use it in GitHub Desktop.
A MutableList- and CollectionLiveData implementation based on Android Architecture Components LiveData which allows to get notified on collection changes.
/*
* Copyright [2019] [Tobias Boehm]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import androidx.lifecycle.MutableLiveData
/**
* This is an implementation of [MutableLiveData] that provides a subset of [MutableCollection] methods which
* change the list content and trigger an observer notification.
*/
open class MutableCollectionLiveData<DataType, ContainerType : MutableCollection<DataType>> : MutableLiveData<ContainerType>(), Collection<DataType>
{
// region Set Member Vars
// =================================================================================================================================================================================================
override val size: Int
get() = throwIfCollectionIsNullOrExecute { it.size }
// endregion
// region Mutable Collection Methods which change state
// =================================================================================================================================================================================================
fun add(element: DataType): Boolean = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.add(element) } }
fun addAll(elements: Collection<DataType>): Boolean = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.addAll(elements) } }
fun clear() = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.clear() } }
fun remove(element: DataType): Boolean = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.remove(element) } }
fun removeAll(elements: Collection<DataType>): Boolean = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.removeAll(elements) } }
fun retainAll(elements: Collection<DataType>): Boolean = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.retainAll(elements) } }
// endregion
// region Collection Methods
// =================================================================================================================================================================================================
override fun contains(element: DataType): Boolean = throwIfCollectionIsNullOrExecute { it.contains(element) }
override fun containsAll(elements: Collection<DataType>): Boolean = throwIfCollectionIsNullOrExecute { it.containsAll(elements) }
override fun isEmpty(): Boolean = throwIfCollectionIsNullOrExecute { it.isEmpty() }
override fun iterator(): Iterator<DataType> = throwIfCollectionIsNullOrExecute { it.iterator() }
// endregion
// region Public Methods
// =================================================================================================================================================================================================
fun isCollectionNotNull() : Boolean = value != null
// endregion
// region Private Methods
// =================================================================================================================================================================================================
protected fun <T> throwIfCollectionIsNullOrExecute(pCode: (collection: ContainerType) -> T): T
{
val collection = value
if (collection == null)
{
throw IllegalStateException("You can't do that on a null list")
}
else
{
return pCode(collection)
}
}
protected fun <T> executeAndNotify(pSet: ContainerType, pCode: (list: ContainerType) -> T): T = pCode(pSet).also { postValue(pSet) }
// endregion
}
/*
* Copyright [2018] [Tobias Boehm]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import androidx.lifecycle.MutableLiveData
/**
* This is an implementation of [MutableLiveData] that provides a subset of [MutableList] methods which
* change the list content and trigger an observer notification.
*/
class MutableListLiveData<E> : MutableCollectionLiveData<E,MutableList<E>>(), List<E>
{
// region MutableList Methods which change state
// =================================================================================================================================================================================================
fun add(index: Int, element: E) = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.add(index, element) } }
fun addAll(index: Int, elements: Collection<E>): Boolean = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.addAll(index, elements) } }
fun removeAt(index: Int): E = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.removeAt(index) } }
fun set(index: Int, element: E): E = throwIfCollectionIsNullOrExecute { list -> executeAndNotify(list) { it.set(index, element) } }
// endregion
// region List Methods
// =================================================================================================================================================================================================
override fun get(index: Int): E = throwIfCollectionIsNullOrExecute { it.get(index) }
override fun indexOf(element: E): Int = throwIfCollectionIsNullOrExecute { it.indexOf(element) }
override fun lastIndexOf(element: E): Int = throwIfCollectionIsNullOrExecute { it.lastIndexOf(element) }
override fun listIterator(): ListIterator<E> = throwIfCollectionIsNullOrExecute { it.listIterator() }
override fun listIterator(index: Int): ListIterator<E> = throwIfCollectionIsNullOrExecute { it.listIterator(index) }
override fun subList(fromIndex: Int, toIndex: Int): List<E> = throwIfCollectionIsNullOrExecute { it.subList(fromIndex, toIndex) }
// endregion
}
@ToBoehmK
Copy link
Author

ToBoehmK commented Jun 8, 2018

copyright added; package declaration removed;

@ToBoehmK
Copy link
Author

MutableCollectionLiveData.kt added - it can also be used for sets

@ArnyminerZ
Copy link

Example usage that works for me:

Inside view model:

val locations: MutableListLiveData<LatLng> = MutableListLiveData<LatLng>().apply {
    postValue(mutableListOf())
}

On the Activity:

viewModel.locations.observe(this as LifecycleOwner) { locations ->
    try {
        val bounds = LatLngBounds.builder()
            .includeAll(locations)
            .build()

        Timber.i("Detected ${locations.size} locations in map.")
        googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30))
    } catch (e: IllegalStateException) {
        Timber.e("No markers were loaded.")
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment