Paging library : How to transform a PagedList to another PagedList with different size?

Viewed 661

Currently, we have a data structure stored in Room SQLite, named Todo

Our current workflow is as follow

  1. Dao is returning DataSource.Factory<Integer, Todo>
  2. Use LivePagedListBuilder to turn DataSource.Factory<Integer, Todo> into LiveData<PagedList<Todo>>
  3. Observe LiveData<PagedList<Todo>>, and use submitList to pass PagedList<Todo> to PagedListAdapter<Todo, TodoAdapter.ViewHolder>

So far so good for such simple use case.

However, right now, we have a more complex UI requirement. We need to transform Todo to TransformedTodo, before submitList to PagedListAdapter.

Here's our transform function.

List<TransformedTodo> transform(Todo todo)

Note, it is possible to transform 1 Todo, to 1 or many TransformedTodo.

My initial planned workflow is

  1. Dao is returning DataSource.Factory<Integer, Todo>
  2. Use DataSource.Factory.mapByPage, to transform DataSource.Factory<Integer, Todo> to DataSource.Factory<Integer, TransformedTodo>
  3. Use LivePagedListBuilder to turn DataSource.Factory<Integer, TransformedTodo> into LiveData<PagedList<TransformedTodo>>
  4. Observe LiveData<PagedList<TransformedTodo>>, and use submitList to pass PagedList<TransformedTodo> to PagedListAdapter<TransformedTodo, TodoAdapter.ViewHolder>

The tricky part is step 2.

If the transform function, is returning output List<TransformedTodo> where its size is different from input List<Todo>, exception will be thrown.

todosDataSourceFactory.mapByPage(new Function<List<Todo>, List<TransformedTodo>>() {
    @Override
    public List<TransformedTodo> apply(List<Todo> input) {
        // Exception will be thrown if we are returning a List where its size is different from input.
    }
});

The thrown exception is something which looks like

java.lang.IllegalStateException: Invalid Function com.yocto.wetodo.repository.TodoRepository$1@17b6f1b changed return size. This is not supported.

Here's why

// androidx.paging.DataSource

static <A, B> List<B> convert(Function<List<A>, List<B>> function, List<A> source) {
    List<B> dest = function.apply(source);
    if (dest.size() != source.size()) {
        throw new IllegalStateException("Invalid Function " + function
                + " changed return size. This is not supported.");
    }
    return dest;
}

Seem like a limitation in paging library. Is there a way, to transform a PagedList to another PagedList with different size?


Reference links :

https://issuetracker.google.com/issues/142890117

How to use AAC paging library with list size different than list size returned by Room database

1 Answers

TL;DR

I have created a library to allow Paging Library's DataSources page mutation.


The problem here is that Paging Library mapByPage is considered to return the same number of items as the input ones. To be able to return different number of items than the input, a custom mutating function would be required.

Another important thing to note before proceeding, specially when working with Room, is that the DataSource.Factory<Key, Value> will in fact end up generating a PositionalDataSource. Unlike ItemKeyedDataSource and PageKeyedDataSource, it requires that the resulting page items respect the defined pageSize. So, even if we create a mapByPage-like function to allow the mutation of its items, accepting different returned list size, it would still cause problems when working with a PositionalDataSource as it does not allow this list size modification.

A solution that could solve the problem would be to wrap the PositionalDataSource inside another of the two resting DataSources types, which allow to work with non-fixed page size, and make it behave as if it would actually be a PositionalDataSource.

Once we have a non-fixed page size compatible "PositionalDataSource", we can then add the mapByPage-like mutating function.

I have actually created a simple MutableDataSource library which does exactly that. It wraps the PositionalDataSource inside an equally behaving PageKeyedDataSource. Then, a mutateByPage function can be applied, allowing to change the resulting items, even changing the list size or item type. Additionally, I have also added support for mutating the ItemKeyedDataSource and PageKeyedDataSource.

Please, keep in mind this library could still contain bugs, so feel free to report any issue or to contribute.

Related