From e81f00ef1a702cdd02b9a63dbb15ea741d636ca0 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Fri, 19 Apr 2019 16:32:01 +0200 Subject: [PATCH] Updated Hide screen with new arch --- .../topjohnwu/magisk/di/ViewModelsModule.kt | 2 + .../magisk/model/entity/HideAppInfo.kt | 16 +++ .../magisk/model/entity/HideTarget.kt | 10 ++ .../model/entity/recycler/HideRvItem.kt | 92 +++++++++++++ .../model/entity/state/IndeterminateState.kt | 5 + .../topjohnwu/magisk/model/events/RxEvents.kt | 6 + .../topjohnwu/magisk/ui/hide/HideViewModel.kt | 116 ++++++++++++++++ .../magisk/ui/hide/MagiskHideFragment.java | 100 -------------- .../magisk/ui/hide/MagiskHideFragment.kt | 64 +++++++++ .../magisk/utils/DataBindingAdapters.kt | 22 ++++ .../com/topjohnwu/magisk/utils/XAndroid.kt | 50 +++++++ .../java/com/topjohnwu/magisk/utils/XKoin.kt | 20 +++ .../java/com/topjohnwu/magisk/utils/XRx.kt | 5 + app/src/main/res/drawable/ic_checked.xml | 11 ++ .../main/res/drawable/ic_indeterminate.xml | 10 ++ app/src/main/res/drawable/ic_unchecked.xml | 10 ++ .../main/res/layout/fragment_magisk_hide.xml | 35 +++-- app/src/main/res/layout/item_hide_app.xml | 124 ++++++++++++++++++ app/src/main/res/layout/item_hide_process.xml | 51 +++++++ gradle/wrapper/gradle-wrapper.properties | 4 +- 20 files changed, 639 insertions(+), 114 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/entity/HideAppInfo.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/entity/state/IndeterminateState.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.java create mode 100644 app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/XKoin.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/XRx.kt create mode 100644 app/src/main/res/drawable/ic_checked.xml create mode 100644 app/src/main/res/drawable/ic_indeterminate.xml create mode 100644 app/src/main/res/drawable/ic_unchecked.xml create mode 100644 app/src/main/res/layout/item_hide_app.xml create mode 100644 app/src/main/res/layout/item_hide_process.xml diff --git a/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt index c4538d0f6..85054c749 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt @@ -1,6 +1,7 @@ package com.topjohnwu.magisk.di import com.topjohnwu.magisk.ui.MainViewModel +import com.topjohnwu.magisk.ui.hide.HideViewModel import com.topjohnwu.magisk.ui.home.HomeViewModel import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel import org.koin.androidx.viewmodel.dsl.viewModel @@ -11,4 +12,5 @@ val viewModelModules = module { viewModel { MainViewModel() } viewModel { HomeViewModel(get(), get()) } viewModel { SuperuserViewModel(get(), get(), get()) } + viewModel { HideViewModel(get(), get()) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/HideAppInfo.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/HideAppInfo.kt new file mode 100644 index 000000000..74e58ed48 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/HideAppInfo.kt @@ -0,0 +1,16 @@ +package com.topjohnwu.magisk.model.entity + +import android.content.pm.ApplicationInfo +import android.graphics.drawable.Drawable +import com.topjohnwu.magisk.utils.packageInfo +import com.topjohnwu.magisk.utils.processes + +class HideAppInfo( + val info: ApplicationInfo, + val name: String, + val icon: Drawable +) { + + val processes = info.packageInfo?.processes?.distinct() ?: listOf(info.packageName) + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt new file mode 100644 index 000000000..27573783c --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt @@ -0,0 +1,10 @@ +package com.topjohnwu.magisk.model.entity + +class HideTarget(line: String) { + + private val split = line.split(Regex("\\|"), 2) + + val packageName = split[0] + val process = split.getOrElse(1) { packageName } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt new file mode 100644 index 000000000..61cf4de91 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt @@ -0,0 +1,92 @@ +package com.topjohnwu.magisk.model.entity.recycler + +import com.skoumal.teanity.databinding.ComparableRvItem +import com.skoumal.teanity.extensions.addOnPropertyChangedCallback +import com.skoumal.teanity.rxbus.RxBus +import com.skoumal.teanity.util.DiffObservableList +import com.skoumal.teanity.util.KObservableField +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.model.entity.HideAppInfo +import com.topjohnwu.magisk.model.entity.HideTarget +import com.topjohnwu.magisk.model.entity.state.IndeterminateState +import com.topjohnwu.magisk.model.events.HideProcessEvent +import com.topjohnwu.magisk.utils.inject +import com.topjohnwu.magisk.utils.toggle + +class HideRvItem(val item: HideAppInfo, targets: List) : + ComparableRvItem() { + + override val layoutRes: Int = R.layout.item_hide_app + + val packageName = item.info.packageName.orEmpty() + val items = DiffObservableList(callback).also { + val items = item.processes.map { + val isHidden = targets.any { target -> + packageName == target.packageName && it == target.process + } + HideProcessRvItem(packageName, it, isHidden) + } + it.update(items) + } + val isHiddenState = KObservableField(currentState) + val isExpanded = KObservableField(false) + + private val itemsProcess get() = items.filterIsInstance() + + private val currentState + get() = when (itemsProcess.count { it.isHidden.value }) { + items.size -> IndeterminateState.CHECKED + in 1 until items.size -> IndeterminateState.INDETERMINATE + else -> IndeterminateState.UNCHECKED + } + + init { + itemsProcess.forEach { + it.isHidden.addOnPropertyChangedCallback { isHiddenState.value = currentState } + } + } + + fun toggle() { + val desiredState = when (isHiddenState.value) { + IndeterminateState.INDETERMINATE, + IndeterminateState.UNCHECKED -> true + IndeterminateState.CHECKED -> false + } + itemsProcess.forEach { it.isHidden.value = desiredState } + isHiddenState.value = currentState + } + + fun toggleExpansion() { + if (items.size <= 1) return + isExpanded.toggle() + } + + override fun contentSameAs(other: HideRvItem): Boolean = items.all { other.items.contains(it) } + override fun itemSameAs(other: HideRvItem): Boolean = item.info == other.item.info + +} + +class HideProcessRvItem( + val packageName: String, + val process: String, + isHidden: Boolean +) : ComparableRvItem() { + + override val layoutRes: Int = R.layout.item_hide_process + + val isHidden = KObservableField(isHidden) + + private val rxBus: RxBus by inject() + + init { + this.isHidden.addOnPropertyChangedCallback { + rxBus.post(HideProcessEvent(this@HideProcessRvItem)) + } + } + + fun toggle() = isHidden.toggle() + + override fun contentSameAs(other: HideProcessRvItem): Boolean = itemSameAs(other) + override fun itemSameAs(other: HideProcessRvItem): Boolean = + packageName == other.packageName && process == other.process +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/state/IndeterminateState.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/state/IndeterminateState.kt new file mode 100644 index 000000000..ba381e695 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/state/IndeterminateState.kt @@ -0,0 +1,5 @@ +package com.topjohnwu.magisk.model.entity.state + +enum class IndeterminateState { + INDETERMINATE, CHECKED, UNCHECKED +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt new file mode 100644 index 000000000..66317b7e5 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt @@ -0,0 +1,6 @@ +package com.topjohnwu.magisk.model.events + +import com.skoumal.teanity.rxbus.RxBus +import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem + +class HideProcessEvent(val item: HideProcessRvItem) : RxBus.Event \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt new file mode 100644 index 000000000..1611fb0c9 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt @@ -0,0 +1,116 @@ +package com.topjohnwu.magisk.ui.hide + +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import com.skoumal.teanity.databinding.ComparableRvItem +import com.skoumal.teanity.extensions.addOnPropertyChangedCallback +import com.skoumal.teanity.extensions.subscribeK +import com.skoumal.teanity.rxbus.RxBus +import com.skoumal.teanity.util.DiffObservableList +import com.skoumal.teanity.util.KObservableField +import com.topjohnwu.magisk.App +import com.topjohnwu.magisk.BR +import com.topjohnwu.magisk.model.entity.HideAppInfo +import com.topjohnwu.magisk.model.entity.HideTarget +import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem +import com.topjohnwu.magisk.model.entity.recycler.HideRvItem +import com.topjohnwu.magisk.model.events.HideProcessEvent +import com.topjohnwu.magisk.ui.base.MagiskViewModel +import com.topjohnwu.magisk.utils.Utils +import com.topjohnwu.magisk.utils.toSingle +import com.topjohnwu.superuser.Shell +import io.reactivex.Single +import me.tatarka.bindingcollectionadapter2.OnItemBind +import timber.log.Timber + +class HideViewModel( + private val packageManager: PackageManager, + rxBus: RxBus +) : MagiskViewModel() { + + val query = KObservableField("") + val isShowSystem = KObservableField(false) + + private val allItems = DiffObservableList(ComparableRvItem.callback) + val items = DiffObservableList(ComparableRvItem.callback) + val itemBinding = OnItemBind> { itemBinding, _, item -> + item.bind(itemBinding) + itemBinding.bindExtra(BR.viewModel, this@HideViewModel) + } + + init { + rxBus.register() + .subscribeK { toggleItem(it.item) } + .add() + + isShowSystem.addOnPropertyChangedCallback { query() } + query.addOnPropertyChangedCallback { query() } + + refresh() + } + + fun refresh() { + // fetching this for every item is nonsensical, so we add .cache() so the response is all + // the same for every single mapped item, it only actually executes the whole thing the + // first time around. + val hideTargets = Shell.su("magiskhide --ls").toSingle() + .map { it.exec().out } + .flattenAsFlowable { it } + .map { HideTarget(it) } + .toList() + .cache() + + Single.fromCallable { packageManager.getInstalledApplications(0) } + .flattenAsFlowable { it } + .filter { it.enabled && !blacklist.contains(it.packageName) } + .map { + val label = Utils.getAppLabel(it, packageManager) + val icon = it.loadIcon(packageManager) + HideAppInfo(it, label, icon) + } + .filter { it.processes.isNotEmpty() } + .map { HideRvItem(it, hideTargets.blockingGet()) } + .toList() + .map { it.sortBy { it.item.info.name }; it } + .applyViewModel(this) + .subscribeK(onError = Timber::e) { + allItems.update(it) + query() + } + .add() + } + + private fun query(showSystem: Boolean = isShowSystem.value, query: String = this.query.value) { + allItems.toSingle() + .map { it.filterIsInstance() } + .flattenAsFlowable { it } + .filter { it.item.name.contains(query) || it.item.processes.any { it.contains(query) } } + .filter { if (showSystem) true else it.item.info.flags and ApplicationInfo.FLAG_SYSTEM == 0 } + .toList() + .subscribeK { items.update(it) } + .add() + } + + private fun toggleItem(item: HideProcessRvItem) { + val state = if (item.isHidden.value) "add" else "rm" + "magiskhide --%s %s %s".format(state, item.packageName, item.process) + .let { Shell.su(it) } + .toSingle() + .map { it.submit() } + .subscribeK() + } + + companion object { + private val blacklist = listOf( + App.self.packageName, + "android", + "com.android.chrome", + "com.chrome.beta", + "com.chrome.dev", + "com.chrome.canary", + "com.android.webview", + "com.google.android.webview" + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.java b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.java deleted file mode 100644 index c7f4c3356..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.topjohnwu.magisk.ui.hide; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.SearchView; - -import com.topjohnwu.magisk.Config; -import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.model.adapters.ApplicationAdapter; -import com.topjohnwu.magisk.ui.base.BaseFragment; -import com.topjohnwu.magisk.utils.Event; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import butterknife.BindView; - -public class MagiskHideFragment extends BaseFragment { - - @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; - @BindView(R.id.recyclerView) RecyclerView recyclerView; - - private SearchView search; - private ApplicationAdapter adapter; - private SearchView.OnQueryTextListener searchListener; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false); - unbinder = new MagiskHideFragment_ViewBinding(this, view); - - adapter = new ApplicationAdapter(requireActivity()); - recyclerView.setAdapter(adapter); - - mSwipeRefreshLayout.setRefreshing(true); - mSwipeRefreshLayout.setOnRefreshListener(adapter::refresh); - - searchListener = new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - adapter.filter(query); - return false; - } - - @Override - public boolean onQueryTextChange(String newText) { - adapter.filter(newText); - return false; - } - }; - - requireActivity().setTitle(R.string.magiskhide); - - return view; - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_magiskhide, menu); - search = (SearchView) menu.findItem(R.id.app_search).getActionView(); - search.setOnQueryTextListener(searchListener); - menu.findItem(R.id.show_system).setChecked(Config.get(Config.Key.SHOW_SYSTEM_APP)); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.show_system) { - boolean showSystem = !item.isChecked(); - item.setChecked(showSystem); - Config.set(Config.Key.SHOW_SYSTEM_APP, showSystem); - adapter.setShowSystem(showSystem); - adapter.filter(search.getQuery().toString()); - } - return true; - } - - @Override - public int[] getListeningEvents() { - return new int[] {Event.MAGISK_HIDE_DONE}; - } - - @Override - public void onEvent(int event) { - mSwipeRefreshLayout.setRefreshing(false); - adapter.filter(search.getQuery().toString()); - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt new file mode 100644 index 000000000..64f5f0885 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt @@ -0,0 +1,64 @@ +package com.topjohnwu.magisk.ui.hide + +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.widget.SearchView +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.FragmentMagiskHideBinding +import com.topjohnwu.magisk.ui.base.MagiskFragment +import org.koin.androidx.viewmodel.ext.android.viewModel + +class MagiskHideFragment : MagiskFragment(), + SearchView.OnQueryTextListener { + + override val layoutRes: Int = R.layout.fragment_magisk_hide + override val viewModel: HideViewModel by viewModel() + + override fun onStart() { + super.onStart() + setHasOptionsMenu(true) + requireActivity().setTitle(R.string.magiskhide) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_magiskhide, menu) + menu.apply { + (findItem(R.id.app_search).actionView as? SearchView) + ?.setOnQueryTextListener(this@MagiskHideFragment) + + val showSystem = Config.get(Config.Key.SHOW_SYSTEM_APP) + + findItem(R.id.show_system).isChecked = showSystem + viewModel.isShowSystem.value = showSystem + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == R.id.show_system) { + val showSystem = !item.isChecked + item.isChecked = showSystem + Config.set(Config.Key.SHOW_SYSTEM_APP, showSystem) + viewModel.isShowSystem.value = showSystem + //adapter!!.setShowSystem(showSystem) + //adapter!!.filter(search!!.query.toString()) + } + return true + } + + override fun onQueryTextSubmit(query: String?): Boolean { + viewModel.query.value = query.orEmpty() + return false + } + + override fun onQueryTextChange(query: String?): Boolean { + viewModel.query.value = query.orEmpty() + return false + } + + /*override fun onEvent(event: Int) { + //mSwipeRefreshLayout!!.isRefreshing = false + adapter!!.filter(search!!.query.toString()) + }*/ +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt b/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt index 7f95bca87..df442e554 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt @@ -8,6 +8,8 @@ import androidx.appcompat.widget.Toolbar import androidx.databinding.BindingAdapter import androidx.drawerlayout.widget.DrawerLayout import com.google.android.material.navigation.NavigationView +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.model.entity.state.IndeterminateState @BindingAdapter("onNavigationClick") @@ -35,3 +37,23 @@ fun setImageResource(view: AppCompatImageView, @DrawableRes resId: Int) { fun setTint(view: AppCompatImageView, @ColorInt tint: Int) { view.setColorFilter(tint) } + +@BindingAdapter("isChecked") +fun setChecked(view: AppCompatImageView, isChecked: Boolean) { + val state = when (isChecked) { + true -> IndeterminateState.CHECKED + else -> IndeterminateState.UNCHECKED + } + setChecked(view, state) +} + +@BindingAdapter("isChecked") +fun setChecked(view: AppCompatImageView, isChecked: IndeterminateState) { + view.setImageResource( + when (isChecked) { + IndeterminateState.INDETERMINATE -> R.drawable.ic_indeterminate + IndeterminateState.CHECKED -> R.drawable.ic_checked + IndeterminateState.UNCHECKED -> R.drawable.ic_unchecked + } + ) +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt new file mode 100644 index 000000000..bf594c755 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt @@ -0,0 +1,50 @@ +package com.topjohnwu.magisk.utils + +import android.content.pm.ApplicationInfo +import android.content.pm.ComponentInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.pm.PackageManager.* + +val PackageInfo.processes + get() = activities?.processNames.orEmpty() + + services?.processNames.orEmpty() + + receivers?.processNames.orEmpty() + + providers?.processNames.orEmpty() + +val Array.processNames get() = mapNotNull { it.processName } + +val ApplicationInfo.packageInfo: PackageInfo? + get() { + val pm: PackageManager by inject() + + return try { + val request = GET_ACTIVITIES or + GET_SERVICES or + GET_RECEIVERS or + GET_PROVIDERS + pm.getPackageInfo(packageName, request) + } catch (e1: Exception) { + try { + pm.activities(packageName).apply { + services = pm.services(packageName) + receivers = pm.receivers(packageName) + providers = pm.providers(packageName) + } + } catch (e2: Exception) { + null + } + } + } + +fun PackageManager.activities(packageName: String) = + getPackageInfo(packageName, GET_ACTIVITIES) + +fun PackageManager.services(packageName: String) = + getPackageInfo(packageName, GET_SERVICES).services + +fun PackageManager.receivers(packageName: String) = + getPackageInfo(packageName, GET_RECEIVERS).receivers + +fun PackageManager.providers(packageName: String) = + getPackageInfo(packageName, GET_PROVIDERS).providers \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XKoin.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XKoin.kt new file mode 100644 index 000000000..951dd163d --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/XKoin.kt @@ -0,0 +1,20 @@ +package com.topjohnwu.magisk.utils + +import org.koin.core.context.GlobalContext +import org.koin.core.parameter.ParametersDefinition +import org.koin.core.qualifier.Qualifier +import org.koin.core.scope.Scope + +fun getKoin() = GlobalContext.get().koin + +inline fun inject( + qualifier: Qualifier? = null, + scope: Scope? = null, + noinline parameters: ParametersDefinition? = null +) = lazy { get(qualifier, scope, parameters) } + +inline fun get( + qualifier: Qualifier? = null, + scope: Scope? = null, + noinline parameters: ParametersDefinition? = null +): T = getKoin().get(qualifier, scope, parameters) \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XRx.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XRx.kt new file mode 100644 index 000000000..7c59a13cb --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/XRx.kt @@ -0,0 +1,5 @@ +package com.topjohnwu.magisk.utils + +import io.reactivex.Single + +fun T.toSingle() = Single.just(this) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_checked.xml b/app/src/main/res/drawable/ic_checked.xml new file mode 100644 index 000000000..696760bdb --- /dev/null +++ b/app/src/main/res/drawable/ic_checked.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_indeterminate.xml b/app/src/main/res/drawable/ic_indeterminate.xml new file mode 100644 index 000000000..0d821b586 --- /dev/null +++ b/app/src/main/res/drawable/ic_indeterminate.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_unchecked.xml b/app/src/main/res/drawable/ic_unchecked.xml new file mode 100644 index 000000000..e90297236 --- /dev/null +++ b/app/src/main/res/drawable/ic_unchecked.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_magisk_hide.xml b/app/src/main/res/layout/fragment_magisk_hide.xml index 2164701df..81b380f88 100644 --- a/app/src/main/res/layout/fragment_magisk_hide.xml +++ b/app/src/main/res/layout/fragment_magisk_hide.xml @@ -1,25 +1,36 @@ - + xmlns:tools="http://schemas.android.com/tools"> - + + + + + + + app:onRefreshListener="@{() -> viewModel.refresh()}" + app:refreshing="@{viewModel.loading}"> + android:orientation="vertical" + android:padding="@dimen/margin_generic" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/item_hide_app" /> + - + - \ No newline at end of file diff --git a/app/src/main/res/layout/item_hide_app.xml b/app/src/main/res/layout/item_hide_app.xml new file mode 100644 index 000000000..e5f1de0a3 --- /dev/null +++ b/app/src/main/res/layout/item_hide_app.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_hide_process.xml b/app/src/main/res/layout/item_hide_process.xml new file mode 100644 index 000000000..1e40e542c --- /dev/null +++ b/app/src/main/res/layout/item_hide_process.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 98c2c24b0..72e2511cc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Mar 26 00:03:20 EDT 2019 +#Fri Apr 19 09:51:32 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-rc-2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-all.zip