diff --git a/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt index 1bd14e65e..a5c34465b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt @@ -23,7 +23,7 @@ val redesignModule = module { viewModel { RequestViewModel() } viewModel { SafetynetViewModel() } viewModel { SettingsViewModel() } - viewModel { SuperuserViewModel() } + viewModel { SuperuserViewModel(get(), get()) } viewModel { ThemeViewModel() } viewModel { MainViewModel() } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt index f6610c363..219224c9a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt @@ -1,6 +1,9 @@ package com.topjohnwu.magisk.model.entity.recycler import android.graphics.drawable.Drawable +import android.view.View +import android.view.ViewGroup +import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback @@ -11,10 +14,14 @@ import com.topjohnwu.magisk.model.events.PolicyEnableEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.RxBus +import com.topjohnwu.magisk.utils.setRevealed class PolicyRvItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem() { - override val layoutRes: Int = R.layout.item_policy + override val layoutRes: Int = when { + Config.redesign -> R.layout.item_policy_md2 + else -> R.layout.item_policy + } val isExpanded = KObservableField(false) val isEnabled = KObservableField(item.policy == MagiskPolicy.ALLOW) @@ -22,6 +29,22 @@ class PolicyRvItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvIte val shouldLog = KObservableField(item.logging) fun toggle() = isExpanded.toggle() + fun toggleNotify() = shouldNotify.toggle() + fun toggleLog() = shouldLog.toggle() + + fun toggleEnabled() { + if (isExpanded.value) { + return + } + isEnabled.toggle() + } + + fun toggle(view: View) { + toggle() + (view.parent as ViewGroup) + .findViewById(R.id.expand_layout) + .setRevealed(isExpanded.value) + } private val rxBus: RxBus by inject() diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatActivity.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatActivity.kt index 731029d6a..1b68ca148 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatActivity.kt @@ -2,8 +2,11 @@ package com.topjohnwu.magisk.redesign.compat import android.os.Bundle import android.view.View +import android.view.ViewGroup +import androidx.databinding.OnRebindCallback import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment +import androidx.transition.TransitionManager import com.topjohnwu.magisk.R import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.model.events.ViewEvent @@ -27,6 +30,13 @@ abstract class CompatActivity() { + override fun onPreBind(binding: Binding): Boolean { + TransitionManager.beginDelayedTransition(binding.root as ViewGroup) + return super.onPreBind(binding) + } + }) + delegate.onCreate() navigation?.onCreate(savedInstanceState) } diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatFragment.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatFragment.kt index 255933534..af70a6f91 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/compat/CompatFragment.kt @@ -2,7 +2,10 @@ package com.topjohnwu.magisk.redesign.compat import android.os.Bundle import android.view.View +import android.view.ViewGroup +import androidx.databinding.OnRebindCallback import androidx.databinding.ViewDataBinding +import androidx.transition.TransitionManager import com.topjohnwu.magisk.base.BaseFragment import com.topjohnwu.magisk.model.events.ViewEvent @@ -19,6 +22,13 @@ abstract class CompatFragment() { + override fun onPreBind(binding: Binding): Boolean { + TransitionManager.beginDelayedTransition(binding.root as ViewGroup) + return super.onPreBind(binding) + } + }) + delegate.onCreate() } diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/superuser/SuperuserViewModel.kt index 868d200af..5ad78ec21 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/superuser/SuperuserViewModel.kt @@ -1,10 +1,55 @@ package com.topjohnwu.magisk.redesign.superuser +import android.content.pm.PackageManager +import com.topjohnwu.magisk.BR +import com.topjohnwu.magisk.data.database.PolicyDao +import com.topjohnwu.magisk.databinding.ComparableRvItem +import com.topjohnwu.magisk.extensions.applySchedulers +import com.topjohnwu.magisk.extensions.subscribeK +import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.redesign.compat.CompatViewModel +import com.topjohnwu.magisk.redesign.home.itemBindingOf +import com.topjohnwu.magisk.utils.DiffObservableList -class SuperuserViewModel : CompatViewModel() { +class SuperuserViewModel( + private val db: PolicyDao, + private val packageManager: PackageManager +) : CompatViewModel() { + + val items = diffListOf() + val itemBinding = itemBindingOf { + it.bindExtra(BR.viewModel, this) + } + + override fun refresh() = db.fetchAll() + .flattenAsFlowable { it } + .parallel() + .map { PolicyRvItem(it, it.applicationInfo.loadIcon(packageManager)) } + .sequential() + .sorted { o1, o2 -> + compareBy( + { it.item.appName.toLowerCase() }, + { it.item.packageName } + ).compare(o1, o2) + } + .toList() + .map { it to items.calculateDiff(it) } + .applySchedulers() + .applyViewModel(this) + .subscribeK { items.update(it.first, it.second) } fun hidePressed() = Navigation.hide().publish() -} \ No newline at end of file + fun deletePressed(item: PolicyRvItem) { + TODO() + } + +} + +inline fun > diffListOf( + vararg newItems: T +) = DiffObservableList(object : DiffObservableList.Callback { + override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem) + override fun areContentsTheSame(oldItem: T, newItem: T) = oldItem.genericContentSameAs(newItem) +}).also { it.update(newItems.toList()) } \ No newline at end of file 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 255d4965d..6d69a2d05 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt @@ -1,6 +1,10 @@ package com.topjohnwu.magisk.utils +import android.animation.Animator +import android.graphics.drawable.Drawable +import android.os.Build import android.view.View +import android.view.ViewAnimationUtils import android.view.ViewGroup import android.widget.TextSwitcher import android.widget.TextView @@ -9,6 +13,9 @@ import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.Toolbar +import androidx.core.animation.doOnEnd +import androidx.core.view.isInvisible +import androidx.core.view.isVisible import androidx.core.view.postDelayed import androidx.core.view.updateLayoutParams import androidx.databinding.BindingAdapter @@ -16,18 +23,21 @@ import androidx.databinding.InverseBindingAdapter import androidx.databinding.InverseBindingListener import androidx.drawerlayout.widget.DrawerLayout import androidx.interpolator.view.animation.FastOutSlowInInterpolator +import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager.widget.ViewPager import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.navigation.NavigationView import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.extensions.drawableCompat import com.topjohnwu.magisk.extensions.replaceRandomWithSpecial import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.model.entity.state.IndeterminateState import io.reactivex.Observable import io.reactivex.disposables.Disposable import java.util.concurrent.TimeUnit +import kotlin.math.hypot @BindingAdapter("onNavigationClick") @@ -291,4 +301,72 @@ fun View.setMargins( @BindingAdapter("nestedScrollingEnabled") fun RecyclerView.setNestedScrolling(enabled: Boolean) { isNestedScrollingEnabled = enabled +} + +@BindingAdapter("isSelected") +fun View.isSelected(isSelected: Boolean) { + this.isSelected = isSelected +} + +@BindingAdapter("reveal") +fun View.setRevealed(reveal: Boolean) { + val x = measuredWidth + val y = measuredHeight + val maxRadius = hypot(x.toDouble(), y.toDouble()).toFloat() + val start = if (reveal) 0f else maxRadius + val end = if (reveal) maxRadius else 0f + + val anim = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + isInvisible = reveal + return + } else { + ViewAnimationUtils.createCircularReveal(this, x, 0, start, end).apply { + interpolator = FastOutSlowInInterpolator() + setTag(R.id.revealAnim, this) + doOnEnd { setTag(R.id.revealAnim, null) } + } + } + + post { + isVisible = true + anim.start() + } +} + +@BindingAdapter("revealFix") +fun View.setFixReveal(isRevealed: Boolean) { + (getTag(R.id.revealAnim) as? Animator) + ?.doOnEnd { isInvisible = !isRevealed } + ?.let { return } + + isInvisible = !isRevealed +} + +@BindingAdapter("dividerVertical", "dividerHorizontal", requireAll = false) +fun RecyclerView.setDividers(dividerVertical: Int, dividerHorizontal: Int) { + val horizontal = if (dividerHorizontal > 0) { + context.drawableCompat(dividerHorizontal) + } else { + null + } + val vertical = if (dividerVertical > 0) { + context.drawableCompat(dividerVertical) + } else { + null + } + setDividers(vertical, horizontal) +} + +@BindingAdapter("dividerVertical", "dividerHorizontal", requireAll = false) +fun RecyclerView.setDividers(dividerVertical: Drawable?, dividerHorizontal: Drawable?) { + if (dividerHorizontal != null) { + DividerItemDecoration(context, LinearLayoutManager.HORIZONTAL).apply { + setDrawable(dividerHorizontal) + }.let { addItemDecoration(it) } + } + if (dividerVertical != null) { + DividerItemDecoration(context, LinearLayoutManager.VERTICAL).apply { + setDrawable(dividerVertical) + }.let { addItemDecoration(it) } + } } \ No newline at end of file diff --git a/app/src/main/res/color/color_error_primary_transient.xml b/app/src/main/res/color/color_error_primary_transient.xml new file mode 100644 index 000000000..b0fddc1f3 --- /dev/null +++ b/app/src/main/res/color/color_error_primary_transient.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/color_primary_delete_transient.xml b/app/src/main/res/color/color_primary_error_transient.xml similarity index 100% rename from app/src/main/res/color/color_primary_delete_transient.xml rename to app/src/main/res/color/color_primary_error_transient.xml diff --git a/app/src/main/res/drawable-v21/avd_chevron_more.xml b/app/src/main/res/drawable-v21/avd_chevron_more.xml new file mode 100644 index 000000000..91ec08f53 --- /dev/null +++ b/app/src/main/res/drawable-v21/avd_chevron_more.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/avd_more_chevron.xml b/app/src/main/res/drawable-v21/avd_more_chevron.xml new file mode 100644 index 000000000..8c2e437d6 --- /dev/null +++ b/app/src/main/res/drawable-v21/avd_more_chevron.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/ic_more_collapse.xml b/app/src/main/res/drawable-v21/ic_more_collapse.xml new file mode 100644 index 000000000..d436b8308 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_more_collapse.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/divider_l1.xml b/app/src/main/res/drawable/divider_l1.xml new file mode 100644 index 000000000..ec3c9944d --- /dev/null +++ b/app/src/main/res/drawable/divider_l1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_chevron_down_md2.xml b/app/src/main/res/drawable/ic_chevron_down_md2.xml new file mode 100644 index 000000000..c6b418a90 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_down_md2.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_dots_md2.xml b/app/src/main/res/drawable/ic_dots_md2.xml new file mode 100644 index 000000000..ef907257c --- /dev/null +++ b/app/src/main/res/drawable/ic_dots_md2.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home_md2.xml b/app/src/main/res/layout/fragment_home_md2.xml index 8af9b586f..73a80790b 100644 --- a/app/src/main/res/layout/fragment_home_md2.xml +++ b/app/src/main/res/layout/fragment_home_md2.xml @@ -70,7 +70,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_magisk_delete" - app:tint="@color/color_primary_delete_transient" /> + app:tint="@color/color_primary_error_transient" /> + + @@ -20,9 +22,10 @@ android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l2}" tools:layout_marginTop="24dp"> - + android:layout_height="wrap_content" + android:orientation="vertical"> - + + + diff --git a/app/src/main/res/layout/item_policy_md2.xml b/app/src/main/res/layout/item_policy_md2.xml new file mode 100644 index 000000000..508f6920e --- /dev/null +++ b/app/src/main/res/layout/item_policy_md2.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 77b8f273e..71ff03067 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -2,5 +2,6 @@ + \ No newline at end of file