Added implementation of hide screen

Very much wip and doesn't work at all
This commit is contained in:
Viktor De Pasquale
2019-10-30 21:58:42 +01:00
parent 722fba7805
commit f76c020dd7
10 changed files with 426 additions and 26 deletions

View File

@@ -17,7 +17,7 @@ import org.koin.dsl.module
val redesignModule = module {
viewModel { FlashViewModel() }
viewModel { HideViewModel() }
viewModel { HideViewModel(get(), get()) }
viewModel { HomeViewModel(get()) }
viewModel { LogViewModel() }
viewModel { ModuleViewModel() }

View File

@@ -43,35 +43,35 @@ typealias OnErrorListener = (Throwable) -> Unit
/*=== ALIASES FOR OBSERVABLES ===*/
fun <T> Observable<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {}
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {}
) = applySchedulers()
.subscribe(onNext, onError, onComplete)
fun <T> Single<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() },
onNext: OnSuccessListener<T> = {}
onError: OnErrorListener = { it.printStackTrace() },
onNext: OnSuccessListener<T> = {}
) = applySchedulers()
.subscribe(onNext, onError)
fun <T> Maybe<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {}
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {}
) = applySchedulers()
.subscribe(onNext, onError, onComplete)
fun <T> Flowable<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {}
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {}
) = applySchedulers()
.subscribe(onNext, onError, onComplete)
fun Completable.subscribeK(
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {}
onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {}
) = applySchedulers()
.subscribe(onComplete, onError)
@@ -197,5 +197,8 @@ fun <T> ObservableField<T>.toObservable(): Observable<T> {
fun <T : Any> T.toSingle() = Single.just(this)
fun <T1, T2, R> zip(t1: Single<T1>, t2: Single<T2>, zipper: (T1, T2) -> R) =
Single.zip(t1, t2, BiFunction<T1, T2, R> { rt1, rt2 -> zipper(rt1, rt2) })
inline fun <T1, T2, R> zip(
t1: Single<T1>,
t2: Single<T2>,
crossinline zipper: (T1, T2) -> R
) = Single.zip(t1, t2, BiFunction<T1, T2, R> { rt1, rt2 -> zipper(rt1, rt2) })

View File

@@ -14,3 +14,13 @@ class HideAppInfo(
val processes = info.packageInfo?.processes?.distinct() ?: listOf(info.packageName)
}
data class StatefulProcess(
val name: String,
val isHidden: Boolean
)
class ProcessHideApp(
val info: HideAppInfo,
val processes: List<StatefulProcess>
)

View File

@@ -1,18 +1,69 @@
package com.topjohnwu.magisk.model.entity.recycler
import android.view.View
import android.view.ViewGroup
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback
import com.topjohnwu.magisk.extensions.inject
import com.topjohnwu.magisk.extensions.startAnimations
import com.topjohnwu.magisk.extensions.toggle
import com.topjohnwu.magisk.model.entity.HideAppInfo
import com.topjohnwu.magisk.model.entity.HideTarget
import com.topjohnwu.magisk.model.entity.ProcessHideApp
import com.topjohnwu.magisk.model.entity.StatefulProcess
import com.topjohnwu.magisk.model.entity.state.IndeterminateState
import com.topjohnwu.magisk.model.events.HideProcessEvent
import com.topjohnwu.magisk.utils.DiffObservableList
import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.magisk.utils.RxBus
class HideItem(val item: ProcessHideApp) : ComparableRvItem<HideItem>() {
override val layoutRes = R.layout.item_hide_md2
val items = item.processes.map { HideProcessItem(it) }
val isExpanded = KObservableField(false)
val itemsChecked = KObservableField(0)
val isHidden get() = itemsChecked.value == items.size
init {
items.forEach { it.isHidden.addOnPropertyChangedCallback { recalculateChecked() } }
}
fun collapse(v: View) {
(v.parent.parent as? ViewGroup)?.startAnimations()
isExpanded.value = false
}
fun expand(v: View) {
(v.parent as? ViewGroup)?.startAnimations()
isExpanded.value = true
}
private fun recalculateChecked() {
itemsChecked.value = items.count { it.isHidden.value }
}
override fun contentSameAs(other: HideItem): Boolean = item == other.item
override fun itemSameAs(other: HideItem): Boolean = item.info == other.item.info
}
class HideProcessItem(val item: StatefulProcess) : ComparableRvItem<HideProcessItem>() {
override val layoutRes = R.layout.item_hide_process_md2
val isHidden = KObservableField(item.isHidden)
fun toggle() = isHidden.toggle()
override fun contentSameAs(other: HideProcessItem) = item == other.item
override fun itemSameAs(other: HideProcessItem) = item.name == other.item.name
}
class HideRvItem(val item: HideAppInfo, targets: List<HideTarget>) :
ComparableRvItem<HideRvItem>() {

View File

@@ -1,6 +1,7 @@
package com.topjohnwu.magisk.redesign.hide
import android.content.Context
import android.graphics.Insets
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.FragmentHideMd2Binding
import com.topjohnwu.magisk.redesign.compat.CompatFragment
@@ -11,6 +12,8 @@ class HideFragment : CompatFragment<HideViewModel, FragmentHideMd2Binding>() {
override val layoutRes = R.layout.fragment_hide_md2
override val viewModel by viewModel<HideViewModel>()
override fun consumeSystemWindowInsets(insets: Insets) = insets
override fun onAttach(context: Context) {
super.onAttach(context)

View File

@@ -1,5 +1,124 @@
package com.topjohnwu.magisk.redesign.hide
import android.content.pm.ApplicationInfo
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.toSingle
import com.topjohnwu.magisk.model.entity.HideAppInfo
import com.topjohnwu.magisk.model.entity.HideTarget
import com.topjohnwu.magisk.model.entity.ProcessHideApp
import com.topjohnwu.magisk.model.entity.StatefulProcess
import com.topjohnwu.magisk.model.entity.recycler.HideItem
import com.topjohnwu.magisk.model.entity.recycler.HideProcessItem
import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem
import com.topjohnwu.magisk.model.events.HideProcessEvent
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
import com.topjohnwu.magisk.redesign.home.itemBindingOf
import com.topjohnwu.magisk.redesign.superuser.diffListOf
import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.magisk.utils.RxBus
import com.topjohnwu.magisk.utils.currentLocale
import io.reactivex.disposables.Disposable
import java.util.*
class HideViewModel : CompatViewModel()
class HideViewModel(
private val magiskRepo: MagiskRepository,
rxBus: RxBus
) : CompatViewModel() {
@Volatile
private var cache = listOf<HideItem>()
set(value) {
field = Collections.synchronizedList(value)
}
private var queryJob: Disposable? = null
set(value) {
field?.dispose()
field = value
}
val query = KObservableField("")
val isShowSystem = KObservableField(true)
val items = diffListOf<HideItem>()
val itemBinding = itemBindingOf<HideItem> {
it.bindExtra(BR.viewModel, this)
}
val itemInternalBinding = itemBindingOf<HideProcessItem> {
it.bindExtra(BR.viewModel, this)
}
init {
rxBus.register<HideProcessEvent>()
.subscribeK { toggleItem(it.item) }
.add()
}
override fun refresh() = magiskRepo.fetchApps()
.map { it to magiskRepo.fetchHideTargets().blockingGet() }
.map { pair -> pair.first.map { mergeAppTargets(it, pair.second) } }
.flattenAsFlowable { it }
.map { HideItem(it) }
.toList()
.map { it.sort() }
.subscribeK {
cache = it
queryIfNecessary()
}
override fun onCleared() {
queryJob?.dispose()
super.onCleared()
}
// ---
private fun mergeAppTargets(a: HideAppInfo, ts: List<HideTarget>): ProcessHideApp {
val relevantTargets = ts.filter { it.packageName == a.info.packageName }
val processes = a.processes
.map { StatefulProcess(it, relevantTargets.any { i -> it == i.process }) }
return ProcessHideApp(a, processes)
}
private fun List<HideItem>.sort() = sortedWith(compareBy(
{ it.isHidden },
{ it.item.info.name.toLowerCase(currentLocale) },
{ it.item.info.info.packageName }
))
// ---
/** We don't need to re-query when the app count matches. */
private fun queryIfNecessary() {
if (items.size != cache.size) {
query()
}
}
private fun query(
query: String = this.query.value,
showSystem: Boolean = isShowSystem.value
) = cache.toSingle()
.flattenAsFlowable { it }
.parallel()
.filter { showSystem || it.item.info.info.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
.filter {
val inName = it.item.info.name.contains(query, true)
val inPackage = it.item.info.info.packageName.contains(query, true)
val inProcesses = it.item.processes.any { it.name.contains(query, true) }
inName || inPackage || inProcesses
}
.sequential()
.toList()
.map { it to items.calculateDiff(it) }
.subscribeK { items.update(it.first, it.second) }
.let { queryJob = it }
// ---
private fun toggleItem(item: HideProcessRvItem) = magiskRepo
.toggleHide(item.isHidden.value, item.packageName, item.process)
.subscribeK()
.add()
}