Commit 8737be26 authored by Viktor De Pasquale's avatar Viktor De Pasquale

Updated policy (apps) layout to be more compact

Added pinch in to increase list span count / out to decrease
  The setting will be remembered across the whole app (every list that uses Staggered Grid)
Updated indication of whether the policy has root access enabled permitted or not
  Displays crossed out app logo if not permitted
parent eb929160
...@@ -55,6 +55,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -55,6 +55,7 @@ object Config : PreferenceModel, DBConfig {
const val SAFETY = "safety_notice" const val SAFETY = "safety_notice"
const val THEME_ORDINAL = "theme_ordinal" const val THEME_ORDINAL = "theme_ordinal"
const val BOOT_ID = "boot_id" const val BOOT_ID = "boot_id"
const val LIST_SPAN_COUNT = "list_span_count"
// system state // system state
const val MAGISKHIDE = "magiskhide" const val MAGISKHIDE = "magiskhide"
...@@ -109,8 +110,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -109,8 +110,7 @@ object Config : PreferenceModel, DBConfig {
Value.CANARY_DEBUG_CHANNEL Value.CANARY_DEBUG_CHANNEL
else else
Value.CANARY_CHANNEL Value.CANARY_CHANNEL
} } else Value.DEFAULT_CHANNEL
else Value.DEFAULT_CHANNEL
var bootId by preference(Key.BOOT_ID, "") var bootId by preference(Key.BOOT_ID, "")
...@@ -137,6 +137,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -137,6 +137,7 @@ object Config : PreferenceModel, DBConfig {
@JvmStatic @JvmStatic
var coreOnly by preference(Key.COREONLY, false) var coreOnly by preference(Key.COREONLY, false)
var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false) var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false)
var listSpanCount by preference(Key.LIST_SPAN_COUNT, 2)
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "") var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
var locale by preference(Key.LOCALE, "") var locale by preference(Key.LOCALE, "")
...@@ -149,8 +150,9 @@ object Config : PreferenceModel, DBConfig { ...@@ -149,8 +150,9 @@ object Config : PreferenceModel, DBConfig {
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true) var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
// Always return a path in external storage where we can write // Always return a path in external storage where we can write
val downloadDirectory get() = val downloadDirectory
Utils.ensureDownloadPath(downloadPath) ?: get<Context>().getExternalFilesDir(null)!! get() =
Utils.ensureDownloadPath(downloadPath) ?: get<Context>().getExternalFilesDir(null)!!
private const val SU_FINGERPRINT = "su_fingerprint" private const val SU_FINGERPRINT = "su_fingerprint"
......
package com.topjohnwu.magisk.model.entity.recycler package com.topjohnwu.magisk.model.entity.recycler
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.view.View
import android.view.ViewGroup
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback
...@@ -14,8 +12,6 @@ import com.topjohnwu.magisk.model.events.PolicyUpdateEvent ...@@ -14,8 +12,6 @@ import com.topjohnwu.magisk.model.events.PolicyUpdateEvent
import com.topjohnwu.magisk.redesign.superuser.SuperuserViewModel import com.topjohnwu.magisk.redesign.superuser.SuperuserViewModel
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.utils.RxBus
import com.topjohnwu.magisk.utils.rotationTo
import com.topjohnwu.magisk.utils.setRevealed
class PolicyRvItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem<PolicyRvItem>() { class PolicyRvItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem<PolicyRvItem>() {
...@@ -73,18 +69,15 @@ class PolicyItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem< ...@@ -73,18 +69,15 @@ class PolicyItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem<
fun toggle(viewModel: SuperuserViewModel) { fun toggle(viewModel: SuperuserViewModel) {
if (isExpanded.value) { if (isExpanded.value) {
toggle()
return return
} }
isEnabled.toggle() isEnabled.toggle()
viewModel.togglePolicy(this, isEnabled.value) viewModel.togglePolicy(this, isEnabled.value)
} }
fun toggle(view: View) { fun toggle() {
isExpanded.toggle() isExpanded.toggle()
view.rotationTo(if (isExpanded.value) 225 else 180)
(view.parent as ViewGroup)
.findViewById<View>(R.id.policy_expand_container)
.setRevealed(isExpanded.value)
} }
fun toggleNotify(viewModel: SuperuserViewModel) { fun toggleNotify(viewModel: SuperuserViewModel) {
......
...@@ -19,6 +19,7 @@ import com.topjohnwu.magisk.redesign.compat.CompatFragment ...@@ -19,6 +19,7 @@ import com.topjohnwu.magisk.redesign.compat.CompatFragment
import com.topjohnwu.magisk.redesign.compat.hideKeyboard import com.topjohnwu.magisk.redesign.compat.hideKeyboard
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
import com.topjohnwu.magisk.utils.MotionRevealHelper import com.topjohnwu.magisk.utils.MotionRevealHelper
import com.topjohnwu.magisk.utils.PinchZoomTouchListener
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding>(), class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding>(),
...@@ -67,13 +68,18 @@ class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding> ...@@ -67,13 +68,18 @@ class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding>
if (newState != RecyclerView.SCROLL_STATE_IDLE) hideKeyboard() if (newState != RecyclerView.SCROLL_STATE_IDLE) hideKeyboard()
} }
}) })
PinchZoomTouchListener.attachTo(binding.moduleFilterInclude.moduleFilterList)
PinchZoomTouchListener.attachTo(binding.moduleList)
} }
override fun onDestroyView() { override fun onDestroyView() {
listeners.forEach { listeners.forEach {
binding.moduleRemote.removeOnScrollListener(it) binding.moduleList.removeOnScrollListener(it)
binding.moduleFilterInclude.moduleFilterList.removeOnScrollListener(it) binding.moduleFilterInclude.moduleFilterList.removeOnScrollListener(it)
} }
PinchZoomTouchListener.clear(binding.moduleList)
PinchZoomTouchListener.clear(binding.moduleFilterInclude.moduleFilterList)
super.onDestroyView() super.onDestroyView()
} }
...@@ -101,14 +107,14 @@ class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding> ...@@ -101,14 +107,14 @@ class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding>
// --- // ---
override fun onReselected() { override fun onReselected() {
binding.moduleRemote binding.moduleList
.takeIf { .takeIf {
(it.layoutManager as? StaggeredGridLayoutManager)?.let { (it.layoutManager as? StaggeredGridLayoutManager)?.let {
it.findFirstVisibleItemPositions(IntArray(it.spanCount)).min() it.findFirstVisibleItemPositions(IntArray(it.spanCount)).min()
} ?: 0 > 10 } ?: 0 > 10
} }
?.also { it.scrollToPosition(10) } ?.also { it.scrollToPosition(10) }
.let { binding.moduleRemote } .let { binding.moduleList }
.also { it.post { it.smoothScrollToPosition(0) } } .also { it.post { it.smoothScrollToPosition(0) } }
} }
...@@ -117,11 +123,11 @@ class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding> ...@@ -117,11 +123,11 @@ class ModuleFragment : CompatFragment<ModuleViewModel, FragmentModuleMd2Binding>
override fun onPreBind(binding: FragmentModuleMd2Binding) = Unit override fun onPreBind(binding: FragmentModuleMd2Binding) = Unit
private fun setEndlessScroller() { private fun setEndlessScroller() {
val lama = binding.moduleRemote.layoutManager ?: return val lama = binding.moduleList.layoutManager ?: return
lama.isAutoMeasureEnabled = false lama.isAutoMeasureEnabled = false
val listener = EndlessRecyclerScrollListener(lama, viewModel::loadRemote) val listener = EndlessRecyclerScrollListener(lama, viewModel::loadRemote)
binding.moduleRemote.addOnScrollListener(listener) binding.moduleList.addOnScrollListener(listener)
listeners.add(listener) listeners.add(listener)
} }
......
package com.topjohnwu.magisk.redesign.settings package com.topjohnwu.magisk.redesign.settings
import android.graphics.Insets import android.graphics.Insets
import android.os.Bundle
import android.view.View
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.FragmentSettingsMd2Binding import com.topjohnwu.magisk.databinding.FragmentSettingsMd2Binding
import com.topjohnwu.magisk.redesign.compat.CompatFragment import com.topjohnwu.magisk.redesign.compat.CompatFragment
import com.topjohnwu.magisk.utils.PinchZoomTouchListener
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
class SettingsFragment : CompatFragment<SettingsViewModel, FragmentSettingsMd2Binding>() { class SettingsFragment : CompatFragment<SettingsViewModel, FragmentSettingsMd2Binding>() {
...@@ -19,6 +22,16 @@ class SettingsFragment : CompatFragment<SettingsViewModel, FragmentSettingsMd2Bi ...@@ -19,6 +22,16 @@ class SettingsFragment : CompatFragment<SettingsViewModel, FragmentSettingsMd2Bi
activity.title = resources.getString(R.string.section_settings) activity.title = resources.getString(R.string.section_settings)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
PinchZoomTouchListener.attachTo(binding.settingsList)
}
override fun onDestroyView() {
PinchZoomTouchListener.clear(binding.settingsList)
super.onDestroyView()
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
viewModel.items.forEach { it.refresh() } viewModel.items.forEach { it.refresh() }
......
package com.topjohnwu.magisk.redesign.superuser package com.topjohnwu.magisk.redesign.superuser
import android.graphics.Insets import android.graphics.Insets
import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.FragmentSuperuserMd2Binding import com.topjohnwu.magisk.databinding.FragmentSuperuserMd2Binding
import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.redesign.compat.CompatFragment import com.topjohnwu.magisk.redesign.compat.CompatFragment
import com.topjohnwu.magisk.utils.PinchZoomTouchListener
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
class SuperuserFragment : CompatFragment<SuperuserViewModel, FragmentSuperuserMd2Binding>() { class SuperuserFragment : CompatFragment<SuperuserViewModel, FragmentSuperuserMd2Binding>() {
...@@ -23,6 +26,16 @@ class SuperuserFragment : CompatFragment<SuperuserViewModel, FragmentSuperuserMd ...@@ -23,6 +26,16 @@ class SuperuserFragment : CompatFragment<SuperuserViewModel, FragmentSuperuserMd
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
PinchZoomTouchListener.attachTo(binding.superuserList)
}
override fun onDestroyView() {
PinchZoomTouchListener.clear(binding.superuserList)
super.onDestroyView()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_superuser_md2, menu) inflater.inflate(R.menu.menu_superuser_md2, menu)
} }
......
...@@ -448,4 +448,12 @@ fun View.setRotationNotAnimated(rotation: Int) { ...@@ -448,4 +448,12 @@ fun View.setRotationNotAnimated(rotation: Int) {
@BindingAdapter("android:text") @BindingAdapter("android:text")
fun TextView.setTextSafe(text: Int) { fun TextView.setTextSafe(text: Int) {
if (text == 0) this.text = null else setText(text) if (text == 0) this.text = null else setText(text)
}
@BindingAdapter("android:onLongClick")
fun View.setOnLongClickListenerBinding(listener: () -> Unit) {
setOnLongClickListener {
listener()
true
}
} }
\ No newline at end of file
package com.topjohnwu.magisk.utils
import android.view.ScaleGestureDetector
abstract class PinchGestureCallback : ScaleGestureDetector.SimpleOnScaleGestureListener() {
private var startFactor: Float = 1f
override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
startFactor = detector?.scaleFactor ?: 1f
return super.onScaleBegin(detector)
}
override fun onScaleEnd(detector: ScaleGestureDetector?) {
val endFactor = detector?.scaleFactor ?: 1f
if (endFactor > startFactor) onZoom()
else if (endFactor < startFactor) onPinch()
}
abstract fun onPinch()
abstract fun onZoom()
}
\ No newline at end of file
package com.topjohnwu.magisk.utils
import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import androidx.transition.TransitionManager
import com.topjohnwu.magisk.Config
import kotlin.math.max
import kotlin.math.min
class PinchZoomTouchListener private constructor(
private val view: RecyclerView,
private val max: Int = 3,
private val min: Int = 1
) : View.OnTouchListener {
private val layoutManager
get() = view.layoutManager
private val pinchListener = object : PinchGestureCallback() {
override fun onPinch() = updateSpanCount(Config.listSpanCount + 1)
override fun onZoom() = updateSpanCount(Config.listSpanCount - 1)
}
private val gestureDetector by lazy { ScaleGestureDetector(view.context, pinchListener) }
init {
updateSpanCount(Config.listSpanCount, false)
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
gestureDetector.onTouchEvent(event)
return false
}
private fun updateSpanCount(count: Int, animate: Boolean = true) {
if (animate) {
TransitionManager.beginDelayedTransition(view)
}
val boundCount = max(min, min(max, count))
when (val l = layoutManager) {
is StaggeredGridLayoutManager -> l.spanCount = boundCount
is GridLayoutManager -> l.spanCount = boundCount
else -> Unit
}
Config.listSpanCount = boundCount
}
companion object {
@SuppressLint("ClickableViewAccessibility")
fun attachTo(view: RecyclerView) = view.setOnTouchListener(PinchZoomTouchListener(view))
fun clear(view: View) = view.setOnTouchListener(null)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?colorOnSurface"
android:pathData="M16,17H7V10.5C7,8 9,6 11.5,6C14,6 16,8 16,10.5M18,16V10.5C18,7.43 15.86,4.86 13,4.18V3.5A1.5,1.5 0 0,0 11.5,2A1.5,1.5 0 0,0 10,3.5V4.18C7.13,4.86 5,7.43 5,10.5V16L3,18V19H20V18M11.5,22A2,2 0 0,0 13.5,20H9.5A2,2 0 0,0 11.5,22Z" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?colorError"
android:pathData="M22.11 21.46L2.39 1.73L1.11 3L4.06 5.95C2.78 7.63 2 9.72 2 12C2 17.5 6.5 22 12 22C14.28 22 16.37 21.23 18.05 19.94L20.84 22.73L22.11 21.46M12 20C7.58 20 4 16.42 4 12C4 10.27 4.56 8.68 5.5 7.38L16.62 18.5C15.32 19.45 13.73 20 12 20M8.17 4.97L6.72 3.5C8.25 2.56 10.06 2 12 2C17.5 2 22 6.5 22 12C22 13.94 21.44 15.75 20.5 17.28L19.03 15.83C19.65 14.69 20 13.39 20 12C20 7.58 16.42 4 12 4C10.61 4 9.31 4.35 8.17 4.97Z" />
</vector>
\ No newline at end of file
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/module_remote" android:id="@+id/module_list"
adapter="@{viewModel.adapter}" adapter="@{viewModel.adapter}"
dividerHorizontal="@{R.drawable.divider_l1}" dividerHorizontal="@{R.drawable.divider_l1}"
dividerVertical="@{R.drawable.divider_l1}" dividerVertical="@{R.drawable.divider_l1}"
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
dividerVertical="@{R.drawable.divider_l_50}" dividerVertical="@{R.drawable.divider_l_50}"
itemBinding="@{viewModel.itemBinding}" itemBinding="@{viewModel.itemBinding}"
items="@{viewModel.items}" items="@{viewModel.items}"
android:id="@+id/settings_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:focusableInTouchMode="false" android:focusableInTouchMode="false"
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
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"
......
This diff is collapsed.
...@@ -86,8 +86,8 @@ ...@@ -86,8 +86,8 @@
<string name="module_state_restore">Restore</string> <string name="module_state_restore">Restore</string>
<string name="module_action_install_external">Install from storage</string> <string name="module_action_install_external">Install from storage</string>
<string name="superuser_toggle_log">Toggles logging</string> <string name="superuser_toggle_log">Logs</string>
<string name="superuser_toggle_notification">Toggles “toast” 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="hide_filter_hint">Filter by name</string> <string name="hide_filter_hint">Filter by name</string>
......
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