Added empty states for all remaining screens

Fixed some issues in the process as the MergeObservableList doesn't support additions or deletions, duh...
This commit is contained in:
Viktor De Pasquale 2020-01-06 19:03:36 +01:00
parent 7342509b2e
commit 904948dc7d
7 changed files with 107 additions and 32 deletions

View File

@ -10,6 +10,7 @@ import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.binding.BindingAdapter import com.topjohnwu.magisk.model.binding.BindingAdapter
import com.topjohnwu.magisk.model.entity.recycler.ConsoleItem import com.topjohnwu.magisk.model.entity.recycler.ConsoleItem
import com.topjohnwu.magisk.model.entity.recycler.LogItem import com.topjohnwu.magisk.model.entity.recycler.LogItem
import com.topjohnwu.magisk.model.entity.recycler.TextItem
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.redesign.compat.CompatViewModel import com.topjohnwu.magisk.redesign.compat.CompatViewModel
import com.topjohnwu.magisk.redesign.compat.diffListOf import com.topjohnwu.magisk.redesign.compat.diffListOf
@ -27,6 +28,11 @@ class LogViewModel(
private val repo: LogRepository private val repo: LogRepository
) : CompatViewModel() { ) : CompatViewModel() {
// --- empty view
val itemEmpty = TextItem(R.string.log_data_none)
val itemMagiskEmpty = TextItem(R.string.log_data_magisk_none)
// --- main view // --- main view
val items = diffListOf<LogItem>() val items = diffListOf<LogItem>()

View File

@ -2,6 +2,7 @@ package com.topjohnwu.magisk.redesign.module
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.databinding.ObservableArrayList
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
@ -61,6 +62,13 @@ class ModuleViewModel(
private val itemNoneInstalled = TextItem(R.string.module_install_none) private val itemNoneInstalled = TextItem(R.string.module_install_none)
private val itemNoneUpdatable = TextItem(R.string.module_update_none) private val itemNoneUpdatable = TextItem(R.string.module_update_none)
private val itemsInstalledHelpers = ObservableArrayList<TextItem>().also {
it.add(itemNoneInstalled)
}
private val itemsUpdatableHelpers = ObservableArrayList<TextItem>().also {
it.add(itemNoneUpdatable)
}
private val itemsInstalled = diffListOf<ModuleItem>() private val itemsInstalled = diffListOf<ModuleItem>()
private val itemsUpdatable = diffListOf<RepoItem.Update>() private val itemsUpdatable = diffListOf<RepoItem.Update>()
private val itemsRemote = diffListOf<RepoItem.Remote>() private val itemsRemote = diffListOf<RepoItem.Remote>()
@ -68,11 +76,11 @@ class ModuleViewModel(
val adapter = adapterOf<ComparableRvItem<*>>() val adapter = adapterOf<ComparableRvItem<*>>()
val items = MergeObservableList<ComparableRvItem<*>>() val items = MergeObservableList<ComparableRvItem<*>>()
.insertItem(sectionActive) .insertItem(sectionActive)
.insertItem(itemNoneInstalled) .insertList(itemsInstalledHelpers)
.insertList(itemsInstalled) .insertList(itemsInstalled)
.insertItem(InstallModule) .insertItem(InstallModule)
.insertItem(sectionUpdate) .insertItem(sectionUpdate)
.insertItem(itemNoneUpdatable) .insertList(itemsUpdatableHelpers)
.insertList(itemsUpdatable) .insertList(itemsUpdatable)
.insertItem(sectionRemote) .insertItem(sectionRemote)
.insertList(itemsRemote)!! .insertList(itemsRemote)!!
@ -155,13 +163,17 @@ class ModuleViewModel(
.applyViewModel(this) .applyViewModel(this)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnSuccess { itemsInstalled.update(it.first, it.second) } .doOnSuccess { itemsInstalled.update(it.first, it.second) }
.doOnSuccess { if (itemsInstalled.isNotEmpty()) items.remove(itemNoneInstalled) } .doOnSuccess {
if (itemsInstalled.isNotEmpty()) itemsInstalledHelpers.remove(itemNoneInstalled)
}
.observeOn(Schedulers.io()) .observeOn(Schedulers.io())
.map { loadUpdates(it.first) } .map { loadUpdates(it.first) }
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.map { it to itemsUpdatable.calculateDiff(it) } .map { it to itemsUpdatable.calculateDiff(it) }
.doOnSuccess { itemsUpdatable.update(it.first, it.second) } .doOnSuccess { itemsUpdatable.update(it.first, it.second) }
.doOnSuccess { if (itemsUpdatable.isNotEmpty()) items.remove(itemNoneUpdatable) } .doOnSuccess {
if (itemsUpdatable.isNotEmpty()) itemsUpdatableHelpers.remove(itemNoneUpdatable)
}
.ignoreElement()!! .ignoreElement()!!
fun loadRemoteImplicit() = let { itemsRemote.clear(); itemsSearch.clear() } fun loadRemoteImplicit() = let { itemsRemote.clear(); itemsSearch.clear() }

View File

@ -2,6 +2,7 @@ package com.topjohnwu.magisk.redesign.superuser
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import androidx.databinding.ObservableArrayList
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.data.database.PolicyDao
@ -12,17 +13,20 @@ import com.topjohnwu.magisk.extensions.toggle
import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.entity.recycler.PolicyItem import com.topjohnwu.magisk.model.entity.recycler.PolicyItem
import com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem import com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem
import com.topjohnwu.magisk.model.entity.recycler.TextItem
import com.topjohnwu.magisk.model.events.PolicyUpdateEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.dialog.BiometricDialog import com.topjohnwu.magisk.model.events.dialog.BiometricDialog
import com.topjohnwu.magisk.model.events.dialog.SuperuserRevokeDialog import com.topjohnwu.magisk.model.events.dialog.SuperuserRevokeDialog
import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.redesign.compat.CompatViewModel import com.topjohnwu.magisk.redesign.compat.CompatViewModel
import com.topjohnwu.magisk.redesign.compat.adapterOf
import com.topjohnwu.magisk.redesign.compat.diffListOf import com.topjohnwu.magisk.redesign.compat.diffListOf
import com.topjohnwu.magisk.redesign.compat.itemBindingOf import com.topjohnwu.magisk.redesign.compat.itemBindingOf
import com.topjohnwu.magisk.utils.BiometricHelper import com.topjohnwu.magisk.utils.BiometricHelper
import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.currentLocale
import io.reactivex.Single import io.reactivex.Single
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
class SuperuserViewModel( class SuperuserViewModel(
private val db: PolicyDao, private val db: PolicyDao,
@ -30,19 +34,24 @@ class SuperuserViewModel(
private val resources: Resources private val resources: Resources
) : CompatViewModel(), TappableHeadlineItem.Listener { ) : CompatViewModel(), TappableHeadlineItem.Listener {
val items = diffListOf<ComparableRvItem<*>>() private val itemNoData = TextItem(R.string.superuser_policy_none)
private val itemsPolicies = diffListOf<PolicyItem>()
private val itemsHelpers = ObservableArrayList<TextItem>().also {
it.add(itemNoData)
}
val adapter = adapterOf<ComparableRvItem<*>>()
val items = MergeObservableList<ComparableRvItem<*>>()
.insertItem(TappableHeadlineItem.Hide)
.insertItem(TappableHeadlineItem.Safetynet)
.insertList(itemsHelpers)
.insertList(itemsPolicies)
val itemBinding = itemBindingOf<ComparableRvItem<*>> { val itemBinding = itemBindingOf<ComparableRvItem<*>> {
it.bindExtra(BR.viewModel, this) it.bindExtra(BR.viewModel, this)
it.bindExtra(BR.listener, this) it.bindExtra(BR.listener, this)
} }
companion object {
private val menuOptions = listOf(
TappableHeadlineItem.Hide,
TappableHeadlineItem.Safetynet
)
}
// --- // ---
override fun refresh() = db.fetchAll() override fun refresh() = db.fetchAll()
@ -57,11 +66,15 @@ class SuperuserViewModel(
).compare(o1, o2) ).compare(o1, o2)
} }
.toList() .toList()
.map { menuOptions + it } .map { it to itemsPolicies.calculateDiff(it) }
.map { it to items.calculateDiff(it) }
.applySchedulers() .applySchedulers()
.applyViewModel(this) .applyViewModel(this)
.subscribeK { items.update(it.first, it.second) } .subscribeK {
itemsPolicies.update(it.first, it.second)
if (itemsPolicies.isNotEmpty()) {
itemsHelpers.remove(itemNoData)
}
}
// --- // ---
@ -77,7 +90,12 @@ class SuperuserViewModel(
fun deletePressed(item: PolicyItem) { fun deletePressed(item: PolicyItem) {
fun updateState() = deletePolicy(item.item) fun updateState() = deletePolicy(item.item)
.subscribeK { items.removeAll { it.genericItemSameAs(item) } } .subscribeK {
itemsPolicies.removeAll { it.genericItemSameAs(item) }
if (itemsPolicies.isEmpty() && itemsHelpers.isEmpty()) {
itemsHelpers.add(itemNoData)
}
}
.add() .add()
if (BiometricHelper.isEnabled) { if (BiometricHelper.isEnabled) {

View File

@ -61,6 +61,20 @@
</com.google.android.material.circularreveal.cardview.CircularRevealCardView> </com.google.android.material.circularreveal.cardview.CircularRevealCardView>
<FrameLayout
gone="@{!viewModel.items.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<include
item="@{viewModel.itemEmpty}"
layout="@layout/item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
</FrameLayout> </FrameLayout>
</layout> </layout>

View File

@ -18,6 +18,8 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/superuser_list"
adapter="@{viewModel.adapter}"
dividerHorizontal="@{R.drawable.divider_l1}" dividerHorizontal="@{R.drawable.divider_l1}"
dividerVertical="@{R.drawable.divider_l1}" dividerVertical="@{R.drawable.divider_l1}"
goneUnless="@{viewModel.loaded || !viewModel.items.empty}" goneUnless="@{viewModel.loaded || !viewModel.items.empty}"
@ -25,7 +27,6 @@
items="@{viewModel.items}" items="@{viewModel.items}"
nestedScrollingEnabled="@{false}" nestedScrollingEnabled="@{false}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:id="@+id/superuser_list"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"

View File

@ -11,24 +11,44 @@
</data> </data>
<HorizontalScrollView <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <HorizontalScrollView
adapter="@{viewModel.consoleAdapter}" android:layout_width="match_parent"
itemBinding="@{viewModel.itemConsoleBinding}" android:layout_height="match_parent">
items="@{viewModel.itemsConsole}"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
android:paddingBottom="@{viewModel.insets.bottom}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_console"
tools:paddingTop="24dp" />
</HorizontalScrollView> <androidx.recyclerview.widget.RecyclerView
adapter="@{viewModel.consoleAdapter}"
itemBinding="@{viewModel.itemConsoleBinding}"
items="@{viewModel.itemsConsole}"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
android:paddingBottom="@{viewModel.insets.bottom}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_console"
tools:paddingTop="24dp" />
</HorizontalScrollView>
<FrameLayout
gone="@{!viewModel.itemsConsole.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<include
item="@{viewModel.itemMagiskEmpty}"
layout="@layout/item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
</FrameLayout>
</layout> </layout>

View File

@ -91,10 +91,14 @@
<string name="superuser_toggle_log">Logs</string> <string name="superuser_toggle_log">Logs</string>
<string name="superuser_toggle_notification">Notifications</string> <string name="superuser_toggle_notification">Notifications</string>
<string name="superuser_toggle_revoke">Revoke</string> <string name="superuser_toggle_revoke">Revoke</string>
<string name="superuser_policy_none">No app has asked for superuser permission yet.</string>
<string name="hide_filter_hint">Filter by name</string> <string name="hide_filter_hint">Filter by name</string>
<string name="hide_scroll_up">Scroll up</string> <string name="hide_scroll_up">Scroll up</string>
<string name="hide_filters">Filters</string> <string name="hide_filters">Filters</string>
<string name="hide_search">Search</string> <string name="hide_search">Search</string>
<string name="log_data_none">You\'re log-free, try using your SU enabled apps more.</string>
<string name="log_data_magisk_none">Magisk logs are empty, that\'s weird.</string>
</resources> </resources>