<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/features/tasks/TaskViewModel.java<|end_filename|>
package com.zhuinden.realmpaginationexample.features.tasks;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.paging.PagedList;

import com.zhuinden.realmpaginationexample.application.Injector;
import com.zhuinden.realmpaginationexample.data.dao.TaskDao;
import com.zhuinden.realmpaginationexample.data.entity.Task;

/**
 * Created by Owner on 2017. 10. 09..
 */

public class TaskViewModel
        extends ViewModel {
    private final TaskDao taskDao;

    private LiveData<PagedList<Task>> liveResults;

    public TaskViewModel() {
        taskDao = Injector.get().taskDao(); // should be provided by ViewModelProviders.Factory
        liveResults = taskDao.tasksSortedByDate().create(0, new PagedList.Config.Builder() //
                .setPageSize(20) //
                .setPrefetchDistance(20) //
                .setEnablePlaceholders(true) //
                .build());
    }

    @Override
    protected void onCleared() {
        super.onCleared();
    }

    public LiveData<PagedList<Task>> getTasks() {
        return liveResults;
    }
}


<|start_filename|>realm-pagination/src/main/java/com/zhuinden/realmpagination/RealmLivePagedListProvider.java<|end_filename|>
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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.
 */

package com.zhuinden.realmpagination;

import android.arch.lifecycle.LiveData;
import android.arch.paging.DataSource;
import android.arch.paging.LivePagedListProvider;
import android.arch.paging.PagedList;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import io.realm.RealmChangeListener;
import io.realm.RealmModel;
import io.realm.RealmResults;

/**
 * Created by Zhuinden on 2017.10.27..
 */

class RealmLivePagedListProvider<T extends RealmModel>
        extends LivePagedListProvider<Integer, T> {
    private RealmPaginationManager realmPaginationManager;
    private final RealmQueryDefinition<T> queryDefinition;

    public RealmLivePagedListProvider(RealmPaginationManager realmPaginationManager, RealmQueryDefinition<T> queryDefinition) {
        this.realmPaginationManager = realmPaginationManager;
        this.queryDefinition = queryDefinition;
    }

    @Override
    @NonNull
    protected DataSource<Integer, T> createDataSource() {
        return new RealmTiledDataSource<>(realmPaginationManager.getWorkerRealm(), queryDefinition);
    }

    @NonNull
    @Override
    public LiveData<PagedList<T>> create(@Nullable Integer initialLoadKey, PagedList.Config config) {
        return new RealmComputableLiveData<PagedList<T>>(realmPaginationManager) {
            @Nullable
            private PagedList<T> mList;
            @Nullable
            private DataSource<Integer, T> mDataSource;

            private RealmResults<T> realmResults;

            @SuppressWarnings("Convert2MethodRef")
            private final DataSource.InvalidatedCallback mCallback = () -> invalidate();

            private final RealmChangeListener<RealmResults<T>> mInvalidationTracker = (realmResults) -> {
                mCallback.onInvalidated();
            };

            @Override
            protected PagedList<T> compute() {
                realmResults = queryDefinition.createResults(realmPaginationManager.getWorkerRealm());
                @Nullable Integer initializeKey = initialLoadKey;
                if(mList != null) {
                    //noinspection unchecked
                    initializeKey = (Integer) mList.getLastKey();
                }

                do {
                    if(mDataSource != null) {
                        mDataSource.removeInvalidatedCallback(mCallback);
                        realmResults.removeChangeListener(mInvalidationTracker);
                    }

                    mDataSource = createDataSource();
                    realmResults.addChangeListener(mInvalidationTracker);
                    mDataSource.addInvalidatedCallback(mCallback);

                    mList = new PagedList.Builder<Integer, T>() //
                            .setDataSource(mDataSource) //
                            .setMainThreadExecutor(realmPaginationManager.getMainThreadExecutor())
                            .setBackgroundThreadExecutor(realmPaginationManager.getRealmQueryExecutor())
                            .setConfig(config)
                            .setInitialKey(initializeKey)
                            .build();
                } while(mList.isDetached());
                return mList;
            }
        }.getLiveData();
    }
}


<|start_filename|>realm-pagination/src/main/java/com/zhuinden/realmpagination/RealmPaginationManager.java<|end_filename|>
/*
 * Copyright (C) 2017 <NAME>
 *
 * 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.
 */
package com.zhuinden.realmpagination;

import android.arch.paging.LivePagedListProvider;
import android.os.Handler;
import android.os.HandlerThread;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import io.realm.Realm;
import io.realm.RealmModel;

/**
 * Created by Zhuinden on 2017.10.27..
 */

public class RealmPaginationManager {
    private final Executor mainThreadExecutor = new RealmMainThreadExecutor();
    private AtomicReference<HandlerThread> handlerThread = new AtomicReference<>();
    private AtomicReference<Handler> handler = new AtomicReference<>();
    private AtomicReference<RealmQueryExecutor> queryExecutor = new AtomicReference<>();
    private AtomicInteger openCount = new AtomicInteger();
    private AtomicReference<Realm> workerRealm = new AtomicReference<>();

    public void open() {
        if(openCount.getAndIncrement() == 0) {
            HandlerThread handlerThread = new HandlerThread("REALM_PAGINATION_THREAD[" + hashCode() + "]");
            this.handlerThread.set(handlerThread);
            //noinspection SynchronizationOnLocalVariableOrMethodParameter
            synchronized(handlerThread) {
                handlerThread.start();
                try {
                    handlerThread.wait();
                } catch(InterruptedException e) {
                    // Ignored
                }
            }
            Handler handler = new Handler(handlerThread.getLooper());
            this.handler.set(handler);
            this.queryExecutor.set(new RealmQueryExecutor(this, handler));
            handler.post(() -> {
                workerRealm.set(Realm.getDefaultInstance()); // TODO: Support other configurations.
            });
        }
    }

    public void close() {
        if(openCount.decrementAndGet() == 0) {
            handler.get().post(() -> {
                workerRealm.get().close();
                workerRealm.set(null);
                handlerThread.get().quit();
                handlerThread.set(null);
                handler.set(null);
            });
        }
    }

    Realm getWorkerRealm() {
        return workerRealm.get();
    }

    Executor getMainThreadExecutor() {
        return mainThreadExecutor;
    }

    public <T extends RealmModel> LivePagedListProvider<Integer, T> createLivePagedListProvider(RealmQueryDefinition<T> queryDefinition) {
        if(!isHandlerThreadOpen()) {
            throw new IllegalStateException("The pagination manager must be opened before a live list is created from it.");
        }
        return new RealmLivePagedListProvider<>(this, queryDefinition);
    }

    RealmQueryExecutor getRealmQueryExecutor() {
        return queryExecutor.get();
    }

    boolean isHandlerThreadOpen() {
        return handlerThread.get() != null;
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/RealmModule.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

import com.zhuinden.realmmanager.RealmManager;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

/**
 * Created by Zhuinden on 2017.09.24..
 */

@Module
public class RealmModule {
    @Provides
    @Singleton
    RealmManager realmManager() {
        return new RealmManager();
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/application/injection/DatabaseModule.java<|end_filename|>
package com.zhuinden.realmpaginationexample.application.injection;

import com.zhuinden.realmpagination.RealmPaginationManager;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

/**
 * Created by Owner on 2017. 10. 09..
 */

@Module
public class DatabaseModule {
    @Provides
    @Singleton
    RealmPaginationManager realmPaginationManager() {
        return new RealmPaginationManager();
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/data/entity/Task.java<|end_filename|>
package com.zhuinden.realmpaginationexample.data.entity;

import android.support.annotation.NonNull;
import android.support.v7.recyclerview.extensions.DiffCallback;

import java.util.Date;

import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;

/**
 * Created by Owner on 2017. 10. 09..
 */
public class Task extends RealmObject {
    public static DiffCallback<Task> DIFF_CALLBACK = new DiffCallback<Task>() {
        @Override
        public boolean areItemsTheSame(@NonNull Task oldItem, @NonNull Task newItem) {
            return oldItem.id == newItem.id;
        }

        @Override
        public boolean areContentsTheSame(@NonNull Task oldItem, @NonNull Task newItem) {
            return oldItem.equals(newItem);
        }
    };

    @PrimaryKey
    private int id;

    private String text;

    private Date date;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) {
            return true;
        }
        if(!(o instanceof Task)) {
            return false;
        }

        Task task = (Task) o;

        if(id != task.id) {
            return false;
        }
        if(text != null ? !text.equals(task.text) : task.text != null) {
            return false;
        }
        return date != null ? date.equals(task.date) : task.date == null;

    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (text != null ? text.hashCode() : 0);
        result = 31 * result + (date != null ? date.hashCode() : 0);
        return result;
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/application/CustomApplication.java<|end_filename|>
package com.zhuinden.realmpaginationexample.application;

import android.app.Application;
import android.os.Handler;
import android.os.Looper;

import com.zhuinden.realmpaginationexample.application.injection.AppComponent;
import com.zhuinden.realmpaginationexample.application.injection.DaggerAppComponent;
import com.zhuinden.realmpaginationexample.data.dao.TaskDao;
import com.zhuinden.realmpaginationexample.data.entity.Task;

import java.util.Date;
import java.util.Random;

import io.realm.Realm;
import io.realm.RealmConfiguration;

/**
 * Created by Owner on 2017. 10. 09..
 */

public class CustomApplication
        extends Application {
    private static CustomApplication INSTANCE;

    AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
        Realm.setDefaultConfiguration(new RealmConfiguration.Builder() //
                                              .deleteRealmIfMigrationNeeded()
                                              .initialData(realm -> {
                                                  Date date = new Date();
                                                  for(Tasks tasks : Tasks.values()) {
                                                      Task task = new Task();
                                                      task.setId(tasks.ordinal() + 1);
                                                      task.setText(tasks.getText());
                                                      task.setDate(date);
                                                      realm.insert(task);
                                                  }
                                              })
                                              .build());
        INSTANCE = this;
        appComponent = DaggerAppComponent.builder() //
                .application(this) //
                .build();

        final TaskDao taskDao = appComponent.taskDao();
        // this is even uglier
        final Random random = new Random();
        Handler handler = new Handler(Looper.getMainLooper());
        handler.postDelayed(() -> {
            Task task = new Task();
            task.setId(random.nextInt(500) + 250);
            task.setText("Poochy");
            task.setDate(new Date());
            taskDao.insert(task);
        }, 2500);
        handler.postDelayed(() -> {
            Task task = new Task();
            task.setId(random.nextInt(500) + 250);
            task.setText("Thomas");
            task.setDate(new Date());
            taskDao.insert(task);
        }, 5000);
        handler.postDelayed(() -> {
            Task task = new Task();
            task.setId(random.nextInt(500) + 250);
            task.setText("Bogus");
            task.setDate(new Date());
            taskDao.insert(task);
        }, 7500);
        handler.postDelayed(() -> {
            Task task = new Task();
            task.setId(random.nextInt(500) + 250);
            task.setText("Anderson");
            task.setDate(new Date());
            taskDao.insert(task);
        }, 10000);
    }

    public static CustomApplication get() {
        return INSTANCE;
    }

    private enum Tasks {
        _1("Figure out how to use Room."),
        _2("Consider applying Dagger-Android to show scoping things."),
        _3("Add a considerably higher number of items so that you can actually paginate."),
        BUBBLES("Bubbles"),
        BUNDLES("Bundles"),
        BUNNY("Bunny"),
        BUTTERCUP("Buttercup"),
        BUTTON("Button"),
        CHIPMUNK("Chipmunk"),
        CINNAMON("Cinnamon"),
        CUDDLES("Cuddles"),
        DAISY("Daisy"),
        DIMPLES("Dimples"),
        HICCUP("Hiccup"),
        HUGGIE("Huggie"),
        JELLYBEAN("Jellybean"),
        JIGGLES("Jiggles"),
        JUJUBE("Jujube"),
        KITKAT("Kitkat"),
        LOLLIPOP("Lollipop"),
        MARSHMALLOW("Marshmallow"),
        MUNCHKIN("Munchkin"),
        NIBBLES("Nibbles"),
        NUGGET("Nugget"),
        PANDA("Panda"),
        PEACHES("Peaches"),
        PICKLES("Pickles"),
        PIXIE("Pixie"),
        POCKET("Pocket"),
        SCHMOOPY("Schmoopy"),
        SKITTLES("Skittles"),
        SNICKERS("Snickers"),
        SNOWBALL("Snowball"),
        SNUGGLES("Snuggles"),
        SQUIGGLE("Squiggle"),
        TAFFY("Taffy"),
        TEACUP("Teacup"),
        TIPSY("Tipsy"),
        TWINKLES("Twinkles"),
        VELVET("Velvet"),
        WAFFLES("Waffles"),
        WIGGLES("Wiggles"),
        WINKY("Winky"),
        BAMBI("Bambi"),
        BARBIE("Barbie"),
        BLOSSOM("Blossom"),
        BLUEBELL("Bluebell"),
        CALYPSO("Calypso"),
        COOKIE("Cookie"),
        CUPCAKE("Cupcake"),
        ELECTRA("Electra"),
        GIGGLES("Giggles"),
        GINGER("Ginger"),
        GOLDILOCKS("Goldilocks"),
        GUMDROP("Gumdrop"),
        HONEYBEE("Honeybee"),
        JASMINE("Jasmine"),
        JULIETTE("Juliette"),
        JUNIPER("Juniper"),
        LADYBUG("Ladybug"),
        LAKSHMI("Lakshmi"),
        MISTY("Misty"),
        NUTMEG("Nutmeg"),
        OLYMPIA("Olympia"),
        PRINCESS("Princess"),
        RUBY("Ruby"),
        TIARA("Tiara"),
        TINKERBELL("Tinkerbell"),
        TRIXIE("Trixie"),
        TWINKLE("Twinkle"),
        VENUS("Venus"),
        AMIGO("Amigo"),
        BANJO("Banjo"),
        COSMO("Cosmo"),
        CROCKET("Crocket"),
        DONATELLO("Donatello"),
        ELECTRO("Electro"),
        ELVIS("Elvis"),
        EURIPIDES("Euripides"),
        FIGARO("Figaro"),
        FONZIE("Fonzie"),
        GALILEO("Galileo"),
        GERONIMO("Geronimo"),
        HENDRIX("Hendrix"),
        HERCULES("Hercules"),
        HOBBES("Hobbes"),
        HOUDINI("Houdini"),
        LANCELOT("Lancelot"),
        MARS("Mars"),
        MOSES("Moses"),
        OZZY("Ozzy"),
        PHARAOH("Pharaoh"),
        PICASSO("Picasso"),
        PRINCE("Prince"),
        REMBRANDT("Rembrandt"),
        ROMEO("Romeo"),
        RUMI("Rumi"),
        SIMBA("Simba"),
        TARZAN("Tarzan"),
        WIZARD("Wizard"),
        ZORRO("Zorro");


        private final String text;

        Tasks(String text) {
            this.text = text;
        }

        public String getText() {
            return text;
        }
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/Injector.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

/**
 * Created by Zhuinden on 2017.09.24..
 */

public enum Injector {
    INSTANCE;

    private SingletonComponent singletonComponent;

    static void setComponent(SingletonComponent singletonComponent) {
        INSTANCE.singletonComponent = singletonComponent;
    }

    public static SingletonComponent get() {
        return INSTANCE.singletonComponent;
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/CustomApplication.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

import android.app.Application;
import android.support.annotation.NonNull;

import com.zhuinden.realmautomigration.RealmAutoMigration;

import io.realm.DynamicRealm;
import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmMigration;

/**
 * Created by Zhuinden on 2017.09.24..
 */

public class CustomApplication
        extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
        Realm.setDefaultConfiguration(new RealmConfiguration.Builder().schemaVersion(7) //
                .migration(new RealmMigration() {
                    @Override
                    public void migrate(@NonNull DynamicRealm realm, long oldVersion, long newVersion) {
                        RealmAutoMigration.migrate(realm);
                    }
                }) //
                .initialData(realm -> {
                    Cat cat = new Cat();
                    for(CatNames catName : CatNames.values()) {
                        cat.setName(catName.getName());
                        realm.insert(cat);
                    }
                }) //
                .build());
        SingletonComponent singletonComponent = DaggerSingletonComponent.create();
        Injector.setComponent(singletonComponent);
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/application/MainActivity.java<|end_filename|>
package com.zhuinden.realmpaginationexample.application;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.ViewGroup;

import com.zhuinden.realmpagination.RealmPaginationManager;
import com.zhuinden.realmpaginationexample.R;
import com.zhuinden.realmpaginationexample.features.tasks.TaskFragment;

public class MainActivity
        extends AppCompatActivity {
    ViewGroup container;
    RealmPaginationManager realmPaginationManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        container = findViewById(R.id.container);
        realmPaginationManager = Injector.get().realmPaginationManager();
        if(savedInstanceState == null) { // TODO: navigation.
            getSupportFragmentManager() //
                    .beginTransaction() //
                    .add(R.id.container, new TaskFragment(), "TASK_FRAGMENT") //
                    .commit();
        }
        realmPaginationManager.open();
    }

    @Override
    protected void onDestroy() {
        realmPaginationManager.close();
        super.onDestroy();
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/Cat.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

import io.realm.RealmObject;
import io.realm.annotations.Index;

/**
 * Created by Zhuinden on 2017.09.24..
 */

public class Cat extends RealmObject {
    @Index
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/ScreenUtils.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

import android.content.res.Resources;
import android.util.DisplayMetrics;

/**
 * Created by Zhuinden on 2017.09.24..
 */

public class ScreenUtils {
    private ScreenUtils() {
    }

    public static float pxToDp(float px){
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        float dp = px / (metrics.densityDpi / 160f);
        return Math.round(dp);
    }

    public static float dpToPx(float dp){
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        float px = dp * (metrics.densityDpi / 160f);
        return Math.round(px);
    }
}


<|start_filename|>realm-singleton-manager-example/src/main/java/com/zhuinden/realmmanagerexample/MainActivity.java<|end_filename|>
package com.zhuinden.realmmanagerexample;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatTextView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;

import com.zhuinden.realmmanager.RealmManager;

import butterknife.BindView;
import butterknife.ButterKnife;
import io.realm.Case;
import io.realm.OrderedRealmCollection;
import io.realm.Realm;
import io.realm.RealmRecyclerViewAdapter;

public class MainActivity
        extends AppCompatActivity {
    RealmManager realmManager;

    @BindView(R.id.search_view)
    SearchView searchView;

    @BindView(R.id.recycler_view)
    RecyclerView recyclerView;

    CatAdapter adapter;

    @SuppressLint("NewAPI")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        realmManager = Injector.get().realmManager();
        realmManager.openLocalInstance();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        setupRecyclerView();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        realmManager.closeLocalInstance();
    }

    //
    private void setupRecyclerView() {
        Realm realm = realmManager.getLocalInstance();
        recyclerView.setLayoutManager(new LinearLayoutManager(this,
                LinearLayoutManager.VERTICAL,
                false));
        adapter = new CatAdapter(realm.where(Cat.class).findAllAsync());
        recyclerView.setAdapter(adapter);
        setupSearchView();
    }

    private void setupSearchView() {
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                adapter.getFilter().filter(query);
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                if(searchView.getQuery().length() == 0) {
                    adapter.getFilter().filter("");
                }
                return true;
            }
        });
    }

    ////
    private static class ViewHolder
            extends RecyclerView.ViewHolder {

        TextView textView;

        public ViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView;
        }

        public void bind(Cat cat) {
            textView.setText(cat.getName());
        }

    }

    private class CatAdapter
            extends RealmRecyclerViewAdapter<Cat, ViewHolder>
            implements Filterable {

        public CatAdapter(@Nullable OrderedRealmCollection<Cat> data) {
            super(data, true);
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            TextView textView = new AppCompatTextView(MainActivity.this); // don't do this at home
            RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            int _16dpInPx = ((Float) ScreenUtils.dpToPx(16)).intValue();
            textView.setPadding(_16dpInPx, _16dpInPx, _16dpInPx, _16dpInPx);
            textView.setLayoutParams(layoutParams);
            return new ViewHolder(textView);
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            // noinspection ConstantConditions
            holder.bind(getData().get(position));
        }

        // filtering
        public void filterResults(String text) {
            Realm realm = realmManager.getLocalInstance();
            text = text == null ? null : text.toLowerCase().trim();
            if(text == null || "".equals(text)) {
                updateData(realm.where(Cat.class).findAllAsync());
            } else {
                updateData(realm
                        .where(Cat.class)
                        .contains(CatFields.NAME, text, Case.INSENSITIVE)
                        .findAllAsync());
            }
        }

        public Filter getFilter() {
            return new CatFilter(this);
        }

        private class CatFilter
                extends Filter {
            private final CatAdapter adapter;

            private CatFilter(CatAdapter adapter) {
                super();
                this.adapter = adapter;
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                return new FilterResults();
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                adapter.filterResults(constraint.toString());
            }
        }
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/application/Injector.java<|end_filename|>
package com.zhuinden.realmpaginationexample.application;

import com.zhuinden.realmpaginationexample.application.injection.AppComponent;

/**
 * Created by Owner on 2017. 10. 09..
 */

public class Injector {
    private Injector() {
    }

    public static AppComponent get() {
        return CustomApplication.get().appComponent;
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/features/tasks/TaskFragment.java<|end_filename|>
package com.zhuinden.realmpaginationexample.features.tasks;

import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.zhuinden.realmpaginationexample.R;

/**
 * Created by Owner on 2017. 10. 09..
 */

public class TaskFragment
        extends Fragment {
    RecyclerView recyclerView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.task_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        recyclerView = view.findViewById(R.id.recycler_view);
        TaskViewModel viewModel = ViewModelProviders.of(this).get(TaskViewModel.class);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
        final TaskAdapter taskAdapter = new TaskAdapter();
        recyclerView.setAdapter(taskAdapter);
        viewModel.getTasks().observe(this, pagedList -> {
            //noinspection Convert2MethodRef
            taskAdapter.setList(pagedList);
        });
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/application/injection/AppComponent.java<|end_filename|>
package com.zhuinden.realmpaginationexample.application.injection;

import com.zhuinden.realmpagination.RealmPaginationManager;
import com.zhuinden.realmpaginationexample.application.CustomApplication;
import com.zhuinden.realmpaginationexample.data.dao.TaskDao;

import javax.inject.Singleton;

import dagger.BindsInstance;
import dagger.Component;

/**
 * Created by Owner on 2017. 10. 09..
 */

@Singleton
@Component(modules = {AppModule.class, DatabaseModule.class})
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        AppComponent.Builder application(CustomApplication application);

        AppComponent build();
    }

    TaskDao taskDao();

    RealmPaginationManager realmPaginationManager();

    void inject(CustomApplication application);
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/application/injection/AppModule.java<|end_filename|>
package com.zhuinden.realmpaginationexample.application.injection;

import android.app.Application;

import com.zhuinden.realmpaginationexample.application.CustomApplication;

import dagger.Module;
import dagger.Provides;

/**
 * Created by Owner on 2017. 10. 09..
 */

@Module
public class AppModule {
    @Provides
    Application application(CustomApplication customApplication) {
        return customApplication;
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/Dog.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

import com.zhuinden.realmautomigration.RealmAutoMigration;

import io.realm.RealmList;
import io.realm.RealmObject;
import io.realm.annotations.Index;
import io.realm.annotations.Required;

/**
 * Created by Owner on 2017. 09. 28..
 */
public class Dog
        extends RealmObject {
    @Index
    private String name;

    @Required
    @Index
    private String ownerName;

    private Cat cat;

    @RealmAutoMigration.MigratedList(listType = Cat.class)
    private RealmList<Cat> manyCats;

    @RealmAutoMigration.MigratedList(listType = String.class)
    @Required
    private RealmList<String> phoneNumbers;
}


<|start_filename|>realm-pagination/src/main/java/com/zhuinden/realmpagination/RealmTiledDataSource.java<|end_filename|>
/*
 * Copyright (C) 2017 <NAME>
 *
 * 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.
 */
package com.zhuinden.realmpagination;

import android.arch.paging.TiledDataSource;
import android.support.annotation.WorkerThread;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import io.realm.Realm;
import io.realm.RealmChangeListener;
import io.realm.RealmModel;
import io.realm.RealmResults;

/**
 * Created by Zhuinden on 2017.10.27..
 */

class RealmTiledDataSource<T extends RealmModel> extends TiledDataSource<T> {
    private final Realm workerRealm;
    private final RealmResults<T> liveResults;

    private final RealmChangeListener<RealmResults<T>> realmChangeListener = results -> {
        invalidate();
    };

    // WORKER THREAD
    public RealmTiledDataSource(Realm workerRealm, RealmQueryDefinition<T> queryDefinition) {
        this.workerRealm = workerRealm;
        this.liveResults = queryDefinition.createResults(workerRealm);
        if(!liveResults.isLoaded()) {
            liveResults.load(); // unavoidable
        }
        this.liveResults.addChangeListener(realmChangeListener);
    }

    @Override
    @WorkerThread
    public int countItems() {
        if(workerRealm.isClosed() || !liveResults.isValid()) {
            return 0;
        }
        return liveResults.size();
    }

    @Override
    @WorkerThread
    public List<T> loadRange(int startPosition, int count) {
        int countItems = countItems();
        if(countItems == 0) {
            return Collections.emptyList();
        }
        List<T> list = new ArrayList<>(count);
        for(int i = startPosition; i < startPosition + count && i < countItems; i++) {
            // noinspection ConstantConditions
            list.add(workerRealm.copyFromRealm(liveResults.get(i)));
        }
        return Collections.unmodifiableList(list);
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/CatNames.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

/**
 * Created by Zhuinden on 2017.09.24..
 */

public enum CatNames {
    BUBBLES("Bubbles"),
    BUNDLES("Bundles"),
    BUNNY("Bunny"),
    BUTTERCUP("Buttercup"),
    BUTTON("Button"),
    CHIPMUNK("Chipmunk"),
    CINNAMON("Cinnamon"),
    CUDDLES("Cuddles"),
    DAISY("Daisy"),
    DIMPLES("Dimples"),
    HICCUP("Hiccup"),
    HUGGIE("Huggie"),
    JELLYBEAN("Jellybean"),
    JIGGLES("Jiggles"),
    JUJUBE("Jujube"),
    KITKAT("Kitkat"),
    LOLLIPOP("Lollipop"),
    MARSHMALLOW("Marshmallow"),
    MUNCHKIN("Munchkin"),
    NIBBLES("Nibbles"),
    NUGGET("Nugget"),
    PANDA("Panda"),
    PEACHES("Peaches"),
    PICKLES("Pickles"),
    PIXIE("Pixie"),
    POCKET("Pocket"),
    SCHMOOPY("Schmoopy"),
    SKITTLES("Skittles"),
    SNICKERS("Snickers"),
    SNOWBALL("Snowball"),
    SNUGGLES("Snuggles"),
    SQUIGGLE("Squiggle"),
    TAFFY("Taffy"),
    TEACUP("Teacup"),
    TIPSY("Tipsy"),
    TWINKLES("Twinkles"),
    VELVET("Velvet"),
    WAFFLES("Waffles"),
    WIGGLES("Wiggles"),
    WINKY("Winky"),
    BAMBI("Bambi"),
    BARBIE("Barbie"),
    BLOSSOM("Blossom"),
    BLUEBELL("Bluebell"),
    CALYPSO("Calypso"),
    COOKIE("Cookie"),
    CUPCAKE("Cupcake"),
    ELECTRA("Electra"),
    GIGGLES("Giggles"),
    GINGER("Ginger"),
    GOLDILOCKS("Goldilocks"),
    GUMDROP("Gumdrop"),
    HONEYBEE("Honeybee"),
    JASMINE("Jasmine"),
    JULIETTE("Juliette"),
    JUNIPER("Juniper"),
    LADYBUG("Ladybug"),
    LAKSHMI("Lakshmi"),
    MISTY("Misty"),
    NUTMEG("Nutmeg"),
    OLYMPIA("Olympia"),
    PRINCESS("Princess"),
    RUBY("Ruby"),
    TIARA("Tiara"),
    TINKERBELL("Tinkerbell"),
    TRIXIE("Trixie"),
    TWINKLE("Twinkle"),
    VENUS("Venus"),
    AMIGO("Amigo"),
    BANJO("Banjo"),
    COSMO("Cosmo"),
    CROCKET("Crocket"),
    DONATELLO("Donatello"),
    ELECTRO("Electro"),
    ELVIS("Elvis"),
    EURIPIDES("Euripides"),
    FIGARO("Figaro"),
    FONZIE("Fonzie"),
    GALILEO("Galileo"),
    GERONIMO("Geronimo"),
    HENDRIX("Hendrix"),
    HERCULES("Hercules"),
    HOBBES("Hobbes"),
    HOUDINI("Houdini"),
    LANCELOT("Lancelot"),
    MARS("Mars"),
    MOSES("Moses"),
    OZZY("Ozzy"),
    PHARAOH("Pharaoh"),
    PICASSO("Picasso"),
    PRINCE("Prince"),
    REMBRANDT("Rembrandt"),
    ROMEO("Romeo"),
    RUMI("Rumi"),
    SIMBA("Simba"),
    TARZAN("Tarzan"),
    WIZARD("Wizard"),
    ZORRO("Zorro");

    private String name;

    private CatNames(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/features/tasks/TaskAdapter.java<|end_filename|>
package com.zhuinden.realmpaginationexample.features.tasks;

import android.arch.paging.PagedListAdapter;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.zhuinden.realmpaginationexample.R;
import com.zhuinden.realmpaginationexample.data.entity.Task;

/**
 * Created by Owner on 2017. 10. 09..
 */

public class TaskAdapter
        extends PagedListAdapter<Task, TaskAdapter.ViewHolder> {
    public TaskAdapter() {
        super(Task.DIFF_CALLBACK);
    }

//    protected TaskAdapter(@NonNull ListAdapterConfig<Task> config) {
//        super(config);
//    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.task_item, parent, false));
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Task task = getItem(position);
        if(task != null) {
            holder.bind(task);
        }
    }

    public static class ViewHolder
            extends RecyclerView.ViewHolder {
        TextView text;

        public ViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.task_text);
        }

        public void bind(@NonNull Task item) {
            text.setText(item.getText());
        }
    }
}


<|start_filename|>realm-auto-migration-example/src/main/java/com/zhuinden/realmautomigrationexample/SingletonComponent.java<|end_filename|>
package com.zhuinden.realmautomigrationexample;

import com.zhuinden.realmmanager.RealmManager;

import javax.inject.Singleton;

import dagger.Component;

/**
 * Created by Zhuinden on 2017.09.24..
 */

@Singleton
@Component(modules = RealmModule.class)
public interface SingletonComponent {
    RealmManager realmManager();
}


<|start_filename|>realm-pagination-example/src/main/java/com/zhuinden/realmpaginationexample/data/dao/TaskDao.java<|end_filename|>
package com.zhuinden.realmpaginationexample.data.dao;

import android.arch.paging.LivePagedListProvider;

import com.zhuinden.realmpagination.RealmPaginationManager;
import com.zhuinden.realmpaginationexample.data.entity.Task;

import javax.inject.Inject;
import javax.inject.Singleton;

import io.realm.Realm;

/**
 * Created by Owner on 2017. 10. 09..
 */
@Singleton
public class TaskDao {
    private final RealmPaginationManager realmPaginationManager;

    @Inject
    TaskDao(RealmPaginationManager realmPaginationManager) {
        this.realmPaginationManager = realmPaginationManager;
    }

    public LivePagedListProvider<Integer, Task> tasksSortedByDate() {
        return realmPaginationManager.createLivePagedListProvider(realm -> realm.where(Task.class) //
                .findAllSorted("date"));  // must be SYNCHRONOUS QUERY!!!
    }

    public void insert(Task task) {
        try(Realm realm = Realm.getDefaultInstance()) {
            boolean isInTransaction = realm.isInTransaction();
            if(!isInTransaction) {
                realm.beginTransaction();
            }
            realm.insertOrUpdate(task);
            if(!isInTransaction) {
                realm.commitTransaction();
            }
        }
    }
}
