2020-03-31 08:53:11 +02:00
|
|
|
package com.topjohnwu.magisk.ui.surequest
|
2019-04-26 21:23:25 +02:00
|
|
|
|
2020-10-22 11:40:47 +02:00
|
|
|
import android.annotation.SuppressLint
|
2021-04-18 11:12:53 +02:00
|
|
|
import android.content.Context
|
2019-04-26 21:23:25 +02:00
|
|
|
import android.content.Intent
|
|
|
|
import android.content.SharedPreferences
|
|
|
|
import android.content.res.Resources
|
|
|
|
import android.graphics.drawable.Drawable
|
2020-10-22 11:40:47 +02:00
|
|
|
import android.os.Bundle
|
2019-04-26 21:23:25 +02:00
|
|
|
import android.os.CountDownTimer
|
2020-10-22 11:40:47 +02:00
|
|
|
import android.view.MotionEvent
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
|
|
|
import android.view.accessibility.AccessibilityEvent
|
|
|
|
import android.view.accessibility.AccessibilityNodeInfo
|
|
|
|
import android.view.accessibility.AccessibilityNodeProvider
|
|
|
|
import android.widget.Toast
|
2020-07-12 12:17:50 +02:00
|
|
|
import androidx.databinding.Bindable
|
2020-07-08 12:13:01 +02:00
|
|
|
import androidx.lifecycle.viewModelScope
|
2020-07-12 12:17:50 +02:00
|
|
|
import com.topjohnwu.magisk.BR
|
2019-04-26 21:23:25 +02:00
|
|
|
import com.topjohnwu.magisk.R
|
2020-08-18 15:31:15 +02:00
|
|
|
import com.topjohnwu.magisk.arch.BaseViewModel
|
2020-01-13 15:01:46 +01:00
|
|
|
import com.topjohnwu.magisk.core.Config
|
|
|
|
import com.topjohnwu.magisk.core.magiskdb.PolicyDao
|
2020-09-11 12:09:01 +02:00
|
|
|
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
2020-08-19 11:05:23 +02:00
|
|
|
import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.ALLOW
|
|
|
|
import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.DENY
|
2020-01-13 15:01:46 +01:00
|
|
|
import com.topjohnwu.magisk.core.su.SuRequestHandler
|
2020-01-28 18:49:59 +01:00
|
|
|
import com.topjohnwu.magisk.core.utils.BiometricHelper
|
2020-08-19 11:05:23 +02:00
|
|
|
import com.topjohnwu.magisk.events.DieEvent
|
2020-09-11 11:31:41 +02:00
|
|
|
import com.topjohnwu.magisk.events.ShowUIEvent
|
2020-09-11 12:09:01 +02:00
|
|
|
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
2021-04-18 11:12:53 +02:00
|
|
|
import com.topjohnwu.magisk.ktx.get
|
|
|
|
import com.topjohnwu.magisk.utils.TextHolder
|
2020-10-22 11:40:47 +02:00
|
|
|
import com.topjohnwu.magisk.utils.Utils
|
2020-07-15 10:21:57 +02:00
|
|
|
import com.topjohnwu.magisk.utils.set
|
2020-07-08 12:13:01 +02:00
|
|
|
import kotlinx.coroutines.launch
|
2019-04-26 21:23:25 +02:00
|
|
|
import me.tatarka.bindingcollectionadapter2.ItemBinding
|
2020-01-13 15:01:46 +01:00
|
|
|
import java.util.concurrent.TimeUnit.SECONDS
|
2019-04-26 21:23:25 +02:00
|
|
|
|
|
|
|
class SuRequestViewModel(
|
2020-09-11 12:09:01 +02:00
|
|
|
policyDB: PolicyDao,
|
2021-04-18 11:12:53 +02:00
|
|
|
private val timeoutPrefs: SharedPreferences
|
2019-09-28 09:50:11 +02:00
|
|
|
) : BaseViewModel() {
|
2019-04-26 21:23:25 +02:00
|
|
|
|
2020-09-11 11:31:41 +02:00
|
|
|
lateinit var icon: Drawable
|
|
|
|
lateinit var title: String
|
|
|
|
lateinit var packageName: String
|
2020-07-15 10:21:57 +02:00
|
|
|
|
2020-07-12 12:17:50 +02:00
|
|
|
@get:Bindable
|
2021-04-18 11:12:53 +02:00
|
|
|
val denyText = DenyText()
|
2020-07-15 10:21:57 +02:00
|
|
|
|
2020-07-12 12:17:50 +02:00
|
|
|
@get:Bindable
|
2020-07-15 10:21:57 +02:00
|
|
|
var selectedItemPosition = 0
|
|
|
|
set(value) = set(value, field, { field = it }, BR.selectedItemPosition)
|
|
|
|
|
2020-07-12 12:17:50 +02:00
|
|
|
@get:Bindable
|
2020-07-15 10:21:57 +02:00
|
|
|
var grantEnabled = false
|
|
|
|
set(value) = set(value, field, { field = it }, BR.grantEnabled)
|
2020-02-12 06:45:10 +01:00
|
|
|
|
2020-10-22 11:40:47 +02:00
|
|
|
@SuppressLint("ClickableViewAccessibility")
|
|
|
|
val grantTouchListener = View.OnTouchListener { _: View, event: MotionEvent ->
|
|
|
|
// Filter obscured touches by consuming them.
|
|
|
|
if (event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED != 0
|
|
|
|
|| event.flags and MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED != 0) {
|
|
|
|
if (event.action == MotionEvent.ACTION_UP) {
|
|
|
|
Utils.toast(R.string.touch_filtered_warning, Toast.LENGTH_SHORT)
|
|
|
|
}
|
|
|
|
return@OnTouchListener Config.suTapjack
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2021-04-18 11:12:53 +02:00
|
|
|
val itemBinding = ItemBinding.of<String>(BR.item, R.layout.item_spinner)
|
2019-05-23 20:01:47 +02:00
|
|
|
|
2021-04-18 11:12:53 +02:00
|
|
|
private val handler = SuRequestHandler(get<Context>().packageManager, policyDB)
|
2020-09-11 12:09:01 +02:00
|
|
|
private lateinit var timer: CountDownTimer
|
2019-04-26 21:23:25 +02:00
|
|
|
|
|
|
|
fun grantPressed() {
|
2020-09-11 12:09:01 +02:00
|
|
|
cancelTimer()
|
2019-11-14 11:42:39 +01:00
|
|
|
if (BiometricHelper.isEnabled) {
|
2020-09-11 12:09:01 +02:00
|
|
|
BiometricEvent {
|
|
|
|
onSuccess {
|
|
|
|
respond(ALLOW)
|
2019-11-14 11:42:39 +01:00
|
|
|
}
|
2020-09-11 12:09:01 +02:00
|
|
|
}.publish()
|
2019-11-14 11:42:39 +01:00
|
|
|
} else {
|
2020-09-11 12:09:01 +02:00
|
|
|
respond(ALLOW)
|
2019-11-14 11:42:39 +01:00
|
|
|
}
|
2019-04-26 21:23:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun denyPressed() {
|
2020-09-11 12:09:01 +02:00
|
|
|
respond(DENY)
|
2019-04-26 21:23:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun spinnerTouched(): Boolean {
|
2020-09-11 12:09:01 +02:00
|
|
|
cancelTimer()
|
2019-04-26 21:23:25 +02:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-08 12:13:01 +02:00
|
|
|
fun handleRequest(intent: Intent) {
|
|
|
|
viewModelScope.launch {
|
2020-09-11 12:09:01 +02:00
|
|
|
if (handler.start(intent))
|
|
|
|
showDialog(handler.policy)
|
|
|
|
else
|
2020-07-08 12:13:01 +02:00
|
|
|
DieEvent().publish()
|
|
|
|
}
|
2020-01-13 15:01:46 +01:00
|
|
|
}
|
2019-04-26 21:23:25 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
private fun showDialog(policy: SuPolicy) {
|
2020-09-11 12:17:43 +02:00
|
|
|
icon = policy.icon
|
2020-09-11 12:09:01 +02:00
|
|
|
title = policy.appName
|
|
|
|
packageName = policy.packageName
|
|
|
|
selectedItemPosition = timeoutPrefs.getInt(policy.packageName, 0)
|
2020-06-19 12:52:25 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
// Set timer
|
|
|
|
val millis = SECONDS.toMillis(Config.suDefaultTimeout.toLong())
|
|
|
|
timer = SuTimer(millis, 1000).apply { start() }
|
2019-04-26 21:23:25 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
// Actually show the UI
|
2020-10-22 11:40:47 +02:00
|
|
|
ShowUIEvent(if (Config.suTapjack) EmptyAccessibilityDelegate else null).publish()
|
2020-09-11 12:09:01 +02:00
|
|
|
}
|
2019-11-12 09:20:07 +01:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
private fun respond(action: Int) {
|
|
|
|
timer.cancel()
|
2020-01-13 15:01:46 +01:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
val pos = selectedItemPosition
|
|
|
|
timeoutPrefs.edit().putInt(handler.policy.packageName, pos).apply()
|
|
|
|
handler.respond(action, Config.Value.TIMEOUT_LIST[pos])
|
2020-09-11 11:31:41 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
// Kill activity after response
|
|
|
|
DieEvent().publish()
|
|
|
|
}
|
2020-06-19 12:52:25 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
private fun cancelTimer() {
|
|
|
|
timer.cancel()
|
2021-04-18 11:12:53 +02:00
|
|
|
denyText.seconds = 0
|
2020-09-11 12:09:01 +02:00
|
|
|
}
|
2019-04-26 21:23:25 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
private inner class SuTimer(
|
|
|
|
private val millis: Long,
|
|
|
|
interval: Long
|
|
|
|
) : CountDownTimer(millis, interval) {
|
2020-06-19 12:52:25 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
override fun onTick(remains: Long) {
|
|
|
|
if (!grantEnabled && remains <= millis - 1000) {
|
|
|
|
grantEnabled = true
|
2020-06-19 12:52:25 +02:00
|
|
|
}
|
2021-04-18 11:12:53 +02:00
|
|
|
denyText.seconds = (remains / 1000).toInt() + 1
|
2020-09-11 12:09:01 +02:00
|
|
|
}
|
2020-06-19 12:52:25 +02:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
override fun onFinish() {
|
2021-04-18 11:12:53 +02:00
|
|
|
denyText.seconds = 0
|
2020-09-11 12:09:01 +02:00
|
|
|
respond(DENY)
|
2019-04-26 21:23:25 +02:00
|
|
|
}
|
2019-11-12 09:20:07 +01:00
|
|
|
|
2020-09-11 12:09:01 +02:00
|
|
|
}
|
2020-10-22 11:40:47 +02:00
|
|
|
|
2021-04-18 11:12:53 +02:00
|
|
|
inner class DenyText : TextHolder() {
|
|
|
|
var seconds = 0
|
|
|
|
set(value) = set(value, field, { field = it }, BR.denyText)
|
|
|
|
|
|
|
|
override val isEmpty get() = false
|
|
|
|
|
|
|
|
override fun getText(resources: Resources): CharSequence {
|
|
|
|
return if (seconds != 0)
|
|
|
|
"${resources.getString(R.string.deny)} ($seconds)"
|
|
|
|
else
|
|
|
|
resources.getString(R.string.deny)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-22 11:40:47 +02:00
|
|
|
// Invisible for accessibility services
|
|
|
|
object EmptyAccessibilityDelegate : View.AccessibilityDelegate() {
|
|
|
|
override fun sendAccessibilityEvent(host: View?, eventType: Int) {}
|
|
|
|
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?) = true
|
|
|
|
override fun sendAccessibilityEventUnchecked(host: View?, event: AccessibilityEvent?) {}
|
|
|
|
override fun dispatchPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) = true
|
|
|
|
override fun onPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
|
|
|
|
override fun onInitializeAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
|
|
|
|
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {}
|
|
|
|
override fun addExtraDataToAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo, extraDataKey: String, arguments: Bundle?) {}
|
|
|
|
override fun onRequestSendAccessibilityEvent(host: ViewGroup?, child: View?, event: AccessibilityEvent?): Boolean = false
|
|
|
|
override fun getAccessibilityNodeProvider(host: View?): AccessibilityNodeProvider? = null
|
|
|
|
}
|
2019-11-12 09:20:07 +01:00
|
|
|
}
|