Commit bcd1064e authored by Viktor De Pasquale's avatar Viktor De Pasquale

Updated superuser fragment to new arch

Fixed theme issues along the way
parent 8a8441c8
...@@ -10,4 +10,5 @@ val applicationModule = module { ...@@ -10,4 +10,5 @@ val applicationModule = module {
single { RxBus() } single { RxBus() }
single { get<Context>().resources } single { get<Context>().resources }
single { get<Context>() as App } single { get<Context>() as App }
single { get<Context>().packageManager }
} }
package com.topjohnwu.magisk.di package com.topjohnwu.magisk.di
import com.topjohnwu.magisk.App
import org.koin.dsl.module import org.koin.dsl.module
val databaseModule = module {} val databaseModule = module {
single { get<App>().DB }
}
...@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.di ...@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.di
import com.topjohnwu.magisk.ui.MainViewModel import com.topjohnwu.magisk.ui.MainViewModel
import com.topjohnwu.magisk.ui.home.HomeViewModel import com.topjohnwu.magisk.ui.home.HomeViewModel
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
...@@ -9,4 +10,5 @@ import org.koin.dsl.module ...@@ -9,4 +10,5 @@ import org.koin.dsl.module
val viewModelModules = module { val viewModelModules = module {
viewModel { MainViewModel() } viewModel { MainViewModel() }
viewModel { HomeViewModel(get(), get()) } viewModel { HomeViewModel(get(), get()) }
viewModel { SuperuserViewModel(get(), get(), get()) }
} }
package com.topjohnwu.magisk.model.entity.recycler
import android.graphics.drawable.Drawable
import com.skoumal.teanity.databinding.ComparableRvItem
import com.skoumal.teanity.util.KObservableField
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.entity.Policy
import com.topjohnwu.magisk.utils.toggle
class PolicyRvItem(val item: Policy, val icon: Drawable) : ComparableRvItem<PolicyRvItem>() {
override val layoutRes: Int = R.layout.item_policy
val isExpanded = KObservableField(false)
val isEnabled = KObservableField(item.policy == Policy.ALLOW)
val shouldNotify = KObservableField(item.notification)
val shouldLog = KObservableField(item.logging)
fun toggle() = isExpanded.toggle()
override fun contentSameAs(other: PolicyRvItem): Boolean = itemSameAs(other)
override fun itemSameAs(other: PolicyRvItem): Boolean = item.uid == other.item.uid
}
\ No newline at end of file
package com.topjohnwu.magisk.model.events package com.topjohnwu.magisk.model.events
import android.app.Activity
import com.skoumal.teanity.viewevents.ViewEvent import com.skoumal.teanity.viewevents.ViewEvent
...@@ -15,3 +16,5 @@ class UninstallEvent : ViewEvent() ...@@ -15,3 +16,5 @@ class UninstallEvent : ViewEvent()
class EnvFixEvent : ViewEvent() class EnvFixEvent : ViewEvent()
class UpdateSafetyNetEvent : ViewEvent() class UpdateSafetyNetEvent : ViewEvent()
class ViewActionEvent(val action: Activity.() -> Unit) : ViewEvent()
\ No newline at end of file
...@@ -11,6 +11,7 @@ import com.ncapdevi.fragnav.FragNavController ...@@ -11,6 +11,7 @@ import com.ncapdevi.fragnav.FragNavController
import com.ncapdevi.fragnav.FragNavTransactionOptions import com.ncapdevi.fragnav.FragNavTransactionOptions
import com.skoumal.teanity.viewevents.ViewEvent import com.skoumal.teanity.viewevents.ViewEvent
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder
import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
import com.topjohnwu.magisk.model.navigation.Navigator import com.topjohnwu.magisk.model.navigation.Navigator
...@@ -62,6 +63,7 @@ abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBin ...@@ -62,6 +63,7 @@ abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBin
super.onEventDispatched(event) super.onEventDispatched(event)
when (event) { when (event) {
is MagiskNavigationEvent -> navigateTo(event) is MagiskNavigationEvent -> navigateTo(event)
is ViewActionEvent -> event.action(this)
} }
} }
......
...@@ -5,6 +5,7 @@ import androidx.databinding.ViewDataBinding ...@@ -5,6 +5,7 @@ import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.skoumal.teanity.view.TeanityFragment import com.skoumal.teanity.view.TeanityFragment
import com.skoumal.teanity.viewevents.ViewEvent import com.skoumal.teanity.viewevents.ViewEvent
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
import com.topjohnwu.magisk.model.navigation.Navigator import com.topjohnwu.magisk.model.navigation.Navigator
import kotlin.reflect.KClass import kotlin.reflect.KClass
...@@ -25,6 +26,7 @@ abstract class MagiskFragment<ViewModel : MagiskViewModel, Binding : ViewDataBin ...@@ -25,6 +26,7 @@ abstract class MagiskFragment<ViewModel : MagiskViewModel, Binding : ViewDataBin
super.onEventDispatched(event) super.onEventDispatched(event)
when (event) { when (event) {
is MagiskNavigationEvent -> navigateTo(event) is MagiskNavigationEvent -> navigateTo(event)
is ViewActionEvent -> event.action(requireActivity())
} }
} }
......
package com.topjohnwu.magisk.ui.base package com.topjohnwu.magisk.ui.base
import android.app.Activity
import com.skoumal.teanity.viewmodel.LoadingViewModel import com.skoumal.teanity.viewmodel.LoadingViewModel
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.utils.Event import com.topjohnwu.magisk.utils.Event
import timber.log.Timber import timber.log.Timber
...@@ -10,4 +12,8 @@ abstract class MagiskViewModel : LoadingViewModel(), Event.AutoListener { ...@@ -10,4 +12,8 @@ abstract class MagiskViewModel : LoadingViewModel(), Event.AutoListener {
override fun onEvent(event: Int) = Timber.i("Event of $event was not handled") override fun onEvent(event: Int) = Timber.i("Event of $event was not handled")
override fun getListeningEvents(): IntArray = intArrayOf() override fun getListeningEvents(): IntArray = intArrayOf()
fun withView(action: Activity.() -> Unit) {
ViewActionEvent(action).publish()
}
} }
package com.topjohnwu.magisk.ui.superuser;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.model.adapters.PolicyAdapter;
import com.topjohnwu.magisk.model.entity.Policy;
import com.topjohnwu.magisk.ui.base.BaseFragment;
import java.util.List;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
public class SuperuserFragment extends BaseFragment {
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
private PackageManager pm;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
unbinder = new SuperuserFragment_ViewBinding(this, view);
pm = requireActivity().getPackageManager();
return view;
}
@Override
public void onStart() {
super.onStart();
requireActivity().setTitle(getString(R.string.superuser));
}
@Override
public void onResume() {
super.onResume();
displayPolicyList();
}
private void displayPolicyList() {
List<Policy> policyList = app.getDB().getPolicyList();
if (policyList.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
recyclerView.setAdapter(new PolicyAdapter(policyList, app.getDB(), pm));
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
}
package com.topjohnwu.magisk.ui.superuser
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.FragmentSuperuserBinding
import com.topjohnwu.magisk.ui.base.MagiskFragment
import org.koin.androidx.viewmodel.ext.android.viewModel
class SuperuserFragment :
MagiskFragment<SuperuserViewModel, FragmentSuperuserBinding>() {
override val layoutRes: Int = R.layout.fragment_superuser
override val viewModel: SuperuserViewModel by viewModel()
override fun onStart() {
super.onStart()
magiskActivity.supportActionBar?.title = getString(R.string.superuser)
}
override fun onResume() {
super.onResume()
viewModel.updatePolicies()
}
}
package com.topjohnwu.magisk.ui.superuser
import android.content.pm.PackageManager
import android.content.res.Resources
import com.skoumal.teanity.databinding.ComparableRvItem
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import com.skoumal.teanity.extensions.applySchedulers
import com.skoumal.teanity.util.DiffObservableList
import com.skoumal.teanity.viewevents.SnackbarEvent
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.database.MagiskDB
import com.topjohnwu.magisk.model.entity.Policy
import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem
import com.topjohnwu.magisk.ui.base.MagiskViewModel
import com.topjohnwu.magisk.utils.FingerprintHelper
import com.topjohnwu.magisk.utils.toggle
import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
import io.reactivex.Single
import me.tatarka.bindingcollectionadapter2.ItemBinding
import timber.log.Timber
class SuperuserViewModel(
private val database: MagiskDB,
private val packageManager: PackageManager,
private val resources: Resources
) : MagiskViewModel() {
val items = DiffObservableList(ComparableRvItem.callback)
val itemBinding = ItemBinding.of<ComparableRvItem<*>> { itemBinding, _, item ->
item.bind(itemBinding)
itemBinding.bindExtra(BR.viewModel, this@SuperuserViewModel)
}
private var ignoreNext: PolicyRvItem? = null
init {
updatePolicies()
}
fun updatePolicies() {
Single.fromCallable { database.policyList }
.flattenAsFlowable { it }
.map { PolicyRvItem(it, it.info.loadIcon(packageManager)).setListeners() }
.toList()
.applySchedulers()
.applyViewModel(this)
.subscribe({
items.update(it)
}, Timber::e)
.add()
}
fun deletePressed(item: PolicyRvItem) {
fun updateState() = deletePolicy(item.item)
.subscribe({
items.remove(item)
}, Timber::e)
.add()
withView {
if (FingerprintHelper.useFingerprint()) {
FingerprintAuthDialog(this) { updateState() }.show()
} else {
CustomAlertDialog(this)
.setTitle(R.string.su_revoke_title)
.setMessage(getString(R.string.su_revoke_msg, item.item.appName))
.setPositiveButton(R.string.yes) { _, _ -> updateState() }
.setNegativeButton(R.string.no_thanks, null)
.setCancelable(true)
.show()
}
}
}
private fun PolicyRvItem.setListeners() = apply {
isEnabled.addOnPropertyChangedCallback {
it ?: return@addOnPropertyChangedCallback
if (ignoreNext == this) {
ignoreNext = null
return@addOnPropertyChangedCallback
}
fun updateState() {
item.policy = if (it) Policy.ALLOW else Policy.DENY
updatePolicy(item)
.map { it.policy == Policy.ALLOW }
.subscribe({
val textId = if (it) R.string.su_snack_grant else R.string.su_snack_deny
val text = resources.getString(textId).format(item.appName)
SnackbarEvent(text).publish()
}, Timber::e)
.add()
}
if (FingerprintHelper.useFingerprint()) {
withView {
FingerprintAuthDialog(this, { updateState() }, {
ignoreNext = this@setListeners
isEnabled.toggle()
}).show()
}
} else {
updateState()
}
}
shouldNotify.addOnPropertyChangedCallback {
it ?: return@addOnPropertyChangedCallback
item.notification = it
updatePolicy(item)
.map { it.notification }
.subscribe({
val textId = if (it) R.string.su_snack_notif_on else R.string.su_snack_notif_off
val text = resources.getString(textId).format(item.appName)
SnackbarEvent(text).publish()
}, Timber::e)
.add()
}
shouldLog.addOnPropertyChangedCallback {
it ?: return@addOnPropertyChangedCallback
item.logging = it
updatePolicy(item)
.map { it.logging }
.subscribe({
val textId = if (it) R.string.su_snack_log_on else R.string.su_snack_log_off
val text = resources.getString(textId).format(item.appName)
SnackbarEvent(text).publish()
}, Timber::e)
.add()
}
}
private fun updatePolicy(policy: Policy) =
Single.fromCallable { database.updatePolicy(policy); policy }
.applySchedulers()
private fun deletePolicy(policy: Policy) =
Single.fromCallable { database.deletePolicy(policy); policy }
.applySchedulers()
}
\ No newline at end of file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools">
android:layout_height="match_parent"
android:orientation="vertical">
<TextView <data>
android:id="@+id/empty_rv"
android:layout_width="match_parent" <variable
android:layout_height="match_parent" name="viewModel"
android:layout_gravity="center" type="com.topjohnwu.magisk.ui.superuser.SuperuserViewModel" />
android:fontFamily="sans-serif-light"
android:gravity="center" </data>
android:text="@string/no_apps_found"
android:textSize="20sp" <FrameLayout
android:textStyle="italic"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:dividerHeight="@dimen/card_divider_space"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> <androidx.recyclerview.widget.RecyclerView
android:id="@+id/superuser_content"
dividerColor="@{@android:color/transparent}"
dividerSize="@{@dimen/margin_generic}"
gone="@{!viewModel.loaded}"
itemBinding="@{viewModel.itemBinding}"
items="@{viewModel.items}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="@dimen/margin_generic"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_policy" />
<ProgressBar
style="@style/Widget.Progress"
gone="@{!viewModel.loading}"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/superuser_empty"
gone="@{!viewModel.loaded || viewModel.items.size &gt; 0}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:fontFamily="sans-serif-light"
android:gravity="center"
android:text="@string/no_apps_found"
android:textSize="20sp"
android:textStyle="italic" />
</FrameLayout>
</layout>
</LinearLayout>
This diff is collapsed.
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
<item name="imageColorTint">@color/dark_secondary_text</item> <item name="imageColorTint">@color/dark_secondary_text</item>
<item name="colorControl">?android:attr/textColorSecondary</item> <item name="colorControl">?android:attr/textColorSecondary</item>
<item name="colorAccentFallback">@color/accentFallback</item> <item name="colorAccentFallback">@color/accentFallback</item>
<item name="android:windowBackground">@android:color/black</item>
</style> </style>
</resources> </resources>
\ No newline at end of file
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<item name="colorAccent">@color/colorSecondary</item> <item name="colorAccent">@color/colorSecondary</item>
<item name="colorSecondary">@color/colorSecondary</item> <item name="colorSecondary">@color/colorSecondary</item>
<item name="colorSecondaryVariant">@color/colorSecondaryDark</item> <item name="colorSecondaryVariant">@color/colorSecondaryDark</item>
<item name="android:windowBackground">@android:color/white</item>
</style> </style>
<style name="ThemeFoundation.Colored.ExtraProps" /> <style name="ThemeFoundation.Colored.ExtraProps" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment