diff --git a/app/src/main/java/com/topjohnwu/magisk/model/permissions/PermissionRequestBuilder.kt b/app/src/main/java/com/topjohnwu/magisk/model/permissions/PermissionRequestBuilder.kt new file mode 100644 index 000000000..7aea7d587 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/permissions/PermissionRequestBuilder.kt @@ -0,0 +1,40 @@ +package com.topjohnwu.magisk.model.permissions + +typealias SimpleCallback = () -> Unit +typealias PermissionRationaleCallback = (List) -> Unit + +class PermissionRequestBuilder { + + private var onSuccessCallback: SimpleCallback = {} + private var onFailureCallback: SimpleCallback = {} + private var onShowRationaleCallback: PermissionRationaleCallback = {} + + fun onSuccess(callback: SimpleCallback) { + onSuccessCallback = callback + } + + fun onFailure(callback: SimpleCallback) { + onFailureCallback = callback + } + + fun onShowRationale(callback: PermissionRationaleCallback) { + onShowRationaleCallback = callback + } + + fun build(): PermissionRequest { + return PermissionRequest(onSuccessCallback, onFailureCallback, onShowRationaleCallback) + } + +} + +class PermissionRequest( + private val onSuccessCallback: SimpleCallback, + private val onFailureCallback: SimpleCallback, + private val onShowRationaleCallback: PermissionRationaleCallback +) { + + fun onSuccess() = onSuccessCallback() + fun onFailure() = onFailureCallback() + fun onShowRationale(permissions: List) = onShowRationaleCallback(permissions) + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt index b47364d17..e79d31941 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt @@ -7,14 +7,21 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.core.net.toUri import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment +import com.karumi.dexter.Dexter +import com.karumi.dexter.MultiplePermissionsReport +import com.karumi.dexter.PermissionToken +import com.karumi.dexter.listener.PermissionRequest +import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.ncapdevi.fragnav.FragNavController import com.ncapdevi.fragnav.FragNavTransactionOptions import com.skoumal.teanity.viewevents.ViewEvent import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.ViewActionEvent import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent import com.topjohnwu.magisk.model.navigation.Navigator +import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.utils.Utils import timber.log.Timber import kotlin.reflect.KClass @@ -64,6 +71,13 @@ abstract class MagiskActivity navigateTo(event) is ViewActionEvent -> event.action(this) + is PermissionEvent -> withPermissions(*event.permissions.toTypedArray()) { + onSuccess { event.callback.onNext(true) } + onFailure { + event.callback.onNext(false) + event.callback.onError(SecurityException("User refused permissions")) + } + } } } @@ -135,6 +149,26 @@ abstract class MagiskActivity Unit) { + val request = PermissionRequestBuilder().apply(builder).build() + Dexter.withActivity(this) + .withPermissions(*permissions) + .withListener(object : MultiplePermissionsListener { + override fun onPermissionsChecked(report: MultiplePermissionsReport?) = + if (report?.areAllPermissionsGranted() == true) { + request.onSuccess() + } else { + request.onFailure() + } + + override fun onPermissionRationaleShouldBeShown( + permissions: MutableList?, + token: PermissionToken? + ) = request.onShowRationale(permissions.orEmpty().map { it.name }) + }) + .check() + } + private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) = customAnimations(options.enter, options.exit, options.popEnter, options.popExit) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt index da231691f..cc398e0d9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt @@ -5,9 +5,11 @@ import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment import com.skoumal.teanity.view.TeanityFragment import com.skoumal.teanity.viewevents.ViewEvent +import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.ViewActionEvent import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent import com.topjohnwu.magisk.model.navigation.Navigator +import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import kotlin.reflect.KClass @@ -27,9 +29,20 @@ abstract class MagiskFragment navigateTo(event) is ViewActionEvent -> event.action(requireActivity()) + is PermissionEvent -> magiskActivity.withPermissions(*event.permissions.toTypedArray()) { + onSuccess { event.callback.onNext(true) } + onFailure { + event.callback.onNext(false) + event.callback.onError(SecurityException("User refused permissions")) + } + } } } + fun withPermissions(vararg permissions: String, builder: PermissionRequestBuilder.() -> Unit) { + magiskActivity.withPermissions(*permissions, builder = builder) + } + fun openLink(url: String) = magiskActivity.openUrl(url) open fun onBackPressed(): Boolean = false diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt index 2df9d2c3d..b3b6c60a2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt @@ -2,8 +2,11 @@ package com.topjohnwu.magisk.ui.base import android.app.Activity import com.skoumal.teanity.viewmodel.LoadingViewModel +import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.ViewActionEvent import com.topjohnwu.magisk.utils.Event +import io.reactivex.Observable +import io.reactivex.subjects.PublishSubject import timber.log.Timber @@ -16,4 +19,8 @@ abstract class MagiskViewModel : LoadingViewModel(), Event.AutoListener { ViewActionEvent(action).publish() } + fun withPermissions(vararg permissions: String): Observable { + val subject = PublishSubject.create() + return subject.doOnSubscribe { PermissionEvent(permissions.toList(), subject).publish() } + } }