Convert injected view models to Kotlin

pull/1020/head
Alex Baker 4 years ago
parent 641b60be9b
commit c3896a4ab1

@ -1,106 +0,0 @@
package org.tasks.tags;
import static com.google.common.collect.Iterables.any;
import static org.tasks.Strings.isNullOrEmpty;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import org.tasks.data.TagData;
import org.tasks.data.TagDataDao;
import org.tasks.tags.CheckBoxTriStates.State;
@SuppressWarnings({"WeakerAccess", "RedundantSuppression"})
public class TagPickerViewModel extends ViewModel {
private final MutableLiveData<List<TagData>> tags = new MutableLiveData<>();
private final CompositeDisposable disposables = new CompositeDisposable();
@Inject TagDataDao tagDataDao;
private final Set<TagData> selected = new HashSet<>();
private final Set<TagData> partiallySelected = new HashSet<>();
private String text;
public void observe(LifecycleOwner owner, Observer<List<TagData>> observer) {
tags.observe(owner, observer);
}
public void setSelected(List<TagData> selected, @Nullable List<TagData> partiallySelected) {
this.selected.addAll(selected);
if (partiallySelected != null) {
this.partiallySelected.addAll(partiallySelected);
}
}
public ArrayList<TagData> getSelected() {
return new ArrayList<>(selected);
}
ArrayList<TagData> getPartiallySelected() {
return new ArrayList<>(partiallySelected);
}
public String getText() {
return text;
}
public void search(String text) {
if (!text.equalsIgnoreCase(this.text)) {
disposables.add(
Single.fromCallable(() -> tagDataDao.searchTags(text))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onUpdate));
}
this.text = text;
}
private void onUpdate(List<TagData> results) {
if (!isNullOrEmpty(text) && !any(results, t -> text.equalsIgnoreCase(t.getName()))) {
results.add(0, new TagData(text));
}
tags.setValue(results);
}
@Override
protected void onCleared() {
super.onCleared();
disposables.dispose();
}
State getState(TagData tagData) {
if (partiallySelected.contains(tagData)) {
return State.PARTIALLY_CHECKED;
}
return selected.contains(tagData) ? State.CHECKED : State.UNCHECKED;
}
State toggle(TagData tagData, boolean checked) {
if (tagData.getId() == null) {
tagData = new TagData(tagData.getName());
tagDataDao.createNew(tagData);
}
partiallySelected.remove(tagData);
if (checked) {
selected.add(tagData);
return State.CHECKED;
} else {
selected.remove(tagData);
return State.UNCHECKED;
}
}
}

@ -0,0 +1,88 @@
package org.tasks.tags
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.TagData
import org.tasks.data.TagDataDao
import org.tasks.tags.CheckBoxTriStates.State
import java.util.*
import javax.inject.Inject
class TagPickerViewModel : ViewModel() {
private val tags = MutableLiveData<List<TagData>>()
private val disposables = CompositeDisposable()
@Inject lateinit var tagDataDao: TagDataDao
private val selected: MutableSet<TagData> = HashSet()
private val partiallySelected: MutableSet<TagData> = HashSet()
var text: String? = null
private set
fun observe(owner: LifecycleOwner, observer: Observer<List<TagData>>) =
tags.observe(owner, observer)
fun setSelected(selected: List<TagData>, partiallySelected: List<TagData>?) {
this.selected.addAll(selected)
if (partiallySelected != null) {
this.partiallySelected.addAll(partiallySelected)
}
}
fun getSelected() = ArrayList(selected)
fun getPartiallySelected() = ArrayList(partiallySelected)
fun search(text: String) {
if (!text.equals(this.text, ignoreCase = true)) {
disposables.add(
Single.fromCallable { tagDataDao.searchTags(text) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { results: List<TagData> -> onUpdate(results.toMutableList()) })
}
this.text = text
}
private fun onUpdate(results: MutableList<TagData>) {
if (!isNullOrEmpty(text) && !results.any { text.equals(it.name, ignoreCase = true) }) {
results.add(0, TagData(text))
}
tags.value = results
}
override fun onCleared() {
super.onCleared()
disposables.dispose()
}
fun getState(tagData: TagData): State {
if (partiallySelected.contains(tagData)) {
return State.PARTIALLY_CHECKED
}
return if (selected.contains(tagData)) State.CHECKED else State.UNCHECKED
}
fun toggle(tagData: TagData, checked: Boolean): State {
var tagData = tagData
if (tagData.id == null) {
tagData = TagData(tagData.name)
tagDataDao.createNew(tagData)
}
partiallySelected.remove(tagData)
return if (checked) {
selected.add(tagData)
State.CHECKED
} else {
selected.remove(tagData)
State.UNCHECKED
}
}
}

@ -1,157 +0,0 @@
package org.tasks.ui;
import static com.todoroo.andlib.utility.AndroidUtilities.assertMainThread;
import static com.todoroo.andlib.utility.AndroidUtilities.assertNotMainThread;
import static com.todoroo.andlib.utility.DateUtilities.now;
import static io.reactivex.Single.fromCallable;
import static org.tasks.data.TaskListQuery.getQuery;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.paging.DataSource.Factory;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
import androidx.sqlite.db.SimpleSQLiteQuery;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao;
import io.reactivex.Completable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import org.tasks.BuildConfig;
import org.tasks.data.SubtaskInfo;
import org.tasks.data.TaskContainer;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
@SuppressWarnings({"WeakerAccess", "RedundantSuppression"})
public class TaskListViewModel extends ViewModel implements Observer<PagedList<TaskContainer>> {
private static final PagedList.Config PAGED_LIST_CONFIG =
new PagedList.Config.Builder().setPageSize(20).build();
@Inject Preferences preferences;
@Inject TaskDao taskDao;
private MutableLiveData<List<TaskContainer>> tasks = new MutableLiveData<>();
private Filter filter;
private boolean manualSortFilter;
private final CompositeDisposable disposable = new CompositeDisposable();
private LiveData<PagedList<TaskContainer>> internal;
public void setFilter(@NonNull Filter filter) {
if (!filter.equals(this.filter)
|| !filter.getSqlQuery().equals(this.filter.getSqlQuery())) {
this.filter = filter;
tasks = new MutableLiveData<>();
invalidate();
}
manualSortFilter =
(filter.supportsManualSort() && preferences.isManualSort())
|| (filter.supportsAstridSorting() && preferences.isAstridSort());
}
public void observe(LifecycleOwner owner, Observer<List<TaskContainer>> observer) {
tasks.observe(owner, observer);
}
public void searchByFilter(Filter filter) {
this.filter = filter;
invalidate();
}
private void removeObserver() {
if (internal != null) {
internal.removeObserver(this);
}
}
public void invalidate() {
assertMainThread();
removeObserver();
if (filter == null) {
return;
}
disposable.add(
Single.fromCallable(taskDao::getSubtaskInfo)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
subtasks -> {
if (manualSortFilter || !preferences.usePagedQueries()) {
performNonPagedQuery(subtasks);
} else {
performPagedListQuery();
}
},
Timber::e));
}
private void performNonPagedQuery(SubtaskInfo subtasks) {
disposable.add(
fromCallable(() -> taskDao.fetchTasks(s -> getQuery(preferences, filter, s), subtasks))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(tasks::postValue, Timber::e));
}
private void performPagedListQuery() {
List<String> queries = getQuery(preferences, filter, new SubtaskInfo());
if (BuildConfig.DEBUG && queries.size() != 1) {
throw new RuntimeException("Invalid queries");
}
SimpleSQLiteQuery query = new SimpleSQLiteQuery(queries.get(0));
Timber.d("paged query: %s", query.getSql());
Factory<Integer, TaskContainer> factory = taskDao.getTaskFactory(query);
LivePagedListBuilder<Integer, TaskContainer> builder =
new LivePagedListBuilder<>(factory, PAGED_LIST_CONFIG);
List<TaskContainer> current = tasks.getValue();
if (current instanceof PagedList) {
Object lastKey = ((PagedList<TaskContainer>) current).getLastKey();
if (lastKey instanceof Integer) {
builder.setInitialLoadKey((Integer) lastKey);
}
}
if (BuildConfig.DEBUG) {
builder.setFetchExecutor(
command ->
Completable.fromAction(
() -> {
assertNotMainThread();
long start = now();
command.run();
Timber.d("*** paged list execution took %sms", now() - start);
})
.subscribeOn(Schedulers.io())
.subscribe());
}
internal = builder.build();
internal.observeForever(this);
}
@Override
protected void onCleared() {
disposable.dispose();
removeObserver();
}
public List<TaskContainer> getValue() {
List<TaskContainer> value = tasks.getValue();
return value != null ? value : Collections.emptyList();
}
@Override
public void onChanged(PagedList<TaskContainer> taskContainers) {
tasks.setValue(taskContainers);
}
}

@ -0,0 +1,129 @@
package org.tasks.ui
import androidx.lifecycle.*
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.sqlite.db.SimpleSQLiteQuery
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.dao.TaskDao
import io.reactivex.Completable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import org.tasks.BuildConfig
import org.tasks.data.SubtaskInfo
import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery.getQuery
import org.tasks.preferences.Preferences
import timber.log.Timber
import javax.inject.Inject
class TaskListViewModel : ViewModel(), Observer<PagedList<TaskContainer>> {
@Inject lateinit var preferences: Preferences
@Inject lateinit var taskDao: TaskDao
private var tasks = MutableLiveData<List<TaskContainer>>()
private var filter: Filter? = null
private var manualSortFilter = false
private val disposable = CompositeDisposable()
private var internal: LiveData<PagedList<TaskContainer>>? = null
fun setFilter(filter: Filter) {
if (filter != this.filter
|| filter.getSqlQuery() != this.filter!!.getSqlQuery()) {
this.filter = filter
tasks = MutableLiveData()
invalidate()
}
manualSortFilter = (filter.supportsManualSort() && preferences.isManualSort
|| filter.supportsAstridSorting() && preferences.isAstridSort)
}
fun observe(owner: LifecycleOwner, observer: Observer<List<TaskContainer>>) =
tasks.observe(owner, observer)
fun searchByFilter(filter: Filter?) {
this.filter = filter
invalidate()
}
private fun removeObserver() = internal?.removeObserver(this)
fun invalidate() {
AndroidUtilities.assertMainThread()
removeObserver()
if (filter == null) {
return
}
disposable.add(
Single.fromCallable { taskDao.getSubtaskInfo() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ subtasks: SubtaskInfo ->
if (manualSortFilter || !preferences.usePagedQueries()) {
performNonPagedQuery(subtasks)
} else {
performPagedListQuery()
}
}) { t: Throwable? -> Timber.e(t) })
}
private fun performNonPagedQuery(subtasks: SubtaskInfo) =
disposable.add(
Single.fromCallable { taskDao.fetchTasks({ s: SubtaskInfo? -> getQuery(preferences, filter!!, s!!) }, subtasks) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ value: List<TaskContainer> -> tasks.postValue(value) }) { t: Throwable? -> Timber.e(t) })
private fun performPagedListQuery() {
val queries = getQuery(preferences, filter!!, SubtaskInfo())
if (BuildConfig.DEBUG && queries.size != 1) {
throw RuntimeException("Invalid queries")
}
val query = SimpleSQLiteQuery(queries[0])
Timber.d("paged query: %s", query.sql)
val factory = taskDao.getTaskFactory(query)
val builder = LivePagedListBuilder(factory, PAGED_LIST_CONFIG)
val current = tasks.value!!
if (current is PagedList<*>) {
val lastKey = (current as PagedList<TaskContainer>).lastKey
if (lastKey is Int) {
builder.setInitialLoadKey(lastKey as Int?)
}
}
if (BuildConfig.DEBUG) {
builder.setFetchExecutor { command: Runnable ->
Completable.fromAction {
AndroidUtilities.assertNotMainThread()
val start = DateUtilities.now()
command.run()
Timber.d("*** paged list execution took %sms", DateUtilities.now() - start)
}
.subscribeOn(Schedulers.io())
.subscribe()
}
}
internal = builder.build()
internal!!.observeForever(this)
}
override fun onCleared() {
disposable.dispose()
removeObserver()
}
val value: List<TaskContainer>
get() = tasks.value ?: emptyList()
override fun onChanged(taskContainers: PagedList<TaskContainer>) {
tasks.value = taskContainers
}
companion object {
private val PAGED_LIST_CONFIG = PagedList.Config.Builder().setPageSize(20).build()
}
}
Loading…
Cancel
Save