mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-01-12 21:14:15 -08:00
Added implementation of hide screen
Very much wip and doesn't work at all
This commit is contained in:
@@ -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() }
|
||||
|
||||
@@ -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) })
|
||||
@@ -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>
|
||||
)
|
||||
@@ -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>() {
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user