Мне нужно запросить разрешение для контактов, и когда приложение запускается, я спрашиваю, в части ViewModel мне нужно вызвать метод, который требует разрешения. Мне нужно проверить разрешение, предоставленное пользователем или нет, а затем позвонить, но для проверки разрешения мне нужно иметь доступ к Activity. в то время как в моей ViewModel у меня нет ссылки на Activity и я не хочу иметь, как я могу решить проблему?
Как проверить разрешение предоставлено в ViewModel?
Ответы (3)
Я только что столкнулся с этой проблемой и решил вместо этого использовать LiveData
.
Основная концепция:
ViewModel имеет LiveData о том, какой запрос разрешения необходимо сделать
ViewModel имеет метод (по сути, обратный вызов), который возвращается, если разрешение предоставлено или нет.
SomeViewModel.kt
:
class SomeViewModel : ViewModel() {
val permissionRequest = MutableLiveData<String>()
fun onPermissionResult(permission: String, granted: Boolean) {
TODO("whatever you need to do")
}
}
FragmentOrActivity.kt
class FragmentOrActivity : FragmentOrActivity() {
private viewModel: SomeViewModel by lazy {
ViewModelProviders.of(this).get(SomeViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
......
viewModel.permissionRequest.observe(this, Observer { permission ->
TODO("ask for permission, and then call viewModel.onPermissionResult aftwewards")
})
......
}
}
Я переработал решение. Объект PermissionRequester
— это все, что вам нужно для запроса разрешений из любой точки, где у вас есть хотя бы контекст приложения. Для выполнения этой работы он использует своего помощника PermissionRequestActivity
.
@Parcelize
class PermissionResult(val permission: String, val state: State) : Parcelable
enum class State { GRANTED, DENIED_TEMPORARILY, DENIED_PERMANENTLY }
typealias Cancellable = () -> Unit
private const val PERMISSIONS_ARGUMENT_KEY = "PERMISSIONS_ARGUMENT_KEY"
private const val REQUEST_CODE_ARGUMENT_KEY = "REQUEST_CODE_ARGUMENT_KEY"
object PermissionRequester {
private val callbackMap = ConcurrentHashMap<Int, (List<PermissionResult>) -> Unit>(1)
private var requestCode = 256
get() {
requestCode = field--
return if (field < 0) 255 else field
}
fun requestPermissions(context: Context, vararg permissions: String, callback: (List<PermissionResult>) -> Unit): Cancellable {
val intent = Intent(context, PermissionRequestActivity::class.java)
.putExtra(PERMISSIONS_ARGUMENT_KEY, permissions)
.putExtra(REQUEST_CODE_ARGUMENT_KEY, requestCode)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
callbackMap[requestCode] = callback
return { callbackMap.remove(requestCode) }
}
internal fun onPermissionResult(responses: List<PermissionResult>, requestCode: Int) {
callbackMap[requestCode]?.invoke(responses)
callbackMap.remove(requestCode)
}
}
class PermissionRequestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
requestPermissions()
}
}
private fun requestPermissions() {
val permissions = intent?.getStringArrayExtra(PERMISSIONS_ARGUMENT_KEY) ?: arrayOf()
val requestCode = intent?.getIntExtra(REQUEST_CODE_ARGUMENT_KEY, -1) ?: -1
when {
permissions.isNotEmpty() && requestCode != -1 -> ActivityCompat.requestPermissions(this, permissions, requestCode)
else -> finishWithResult()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
val permissionResults = grantResults.zip(permissions).map { (grantResult, permission) ->
val state = when {
grantResult == PackageManager.PERMISSION_GRANTED -> State.GRANTED
ActivityCompat.shouldShowRequestPermissionRationale(this, permission) -> State.DENIED_TEMPORARILY
else -> State.DENIED_PERMANENTLY
}
PermissionResult(permission, state)
}
finishWithResult(permissionResults)
}
private fun finishWithResult(permissionResult: List<PermissionResult> = listOf()) {
val requestCode = intent?.getIntExtra(REQUEST_CODE_ARGUMENT_KEY, -1) ?: -1
PermissionRequester.onPermissionResult(permissionResult, requestCode)
finish()
}
}
Использование:
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val cancelRequest: Cancellable = requestPermission()
private fun requestPermission(): Cancellable {
return PermissionRequester.requestPermissions(getApplication(), "android.permission.SEND_SMS") {
if (it.firstOrNull()?.state == State.GRANTED) {
Toast.makeText(getApplication(), "GRANTED", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(getApplication(), "DENIED", Toast.LENGTH_LONG).show()
}
}
}
override fun onCleared() {
super.onCleared()
cancelRequest()
}
}
PermissionRequestActivity
на AppManifest
.
- person Machado; 07.10.2020
Я сделал что-то вроде этого:
создайте абстрактный класс, расширяющий AndroidViewModel, который дает вам доступ к контексту приложения:
abstract class BaseViewModel(application: Application) : AndroidViewModel(application), CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCleared() {
super.onCleared()
job.cancel()
}
}
Теперь создайте свою модель представления, расширив класс BaseViewModel, и у вас будет доступ к контексту приложения.
class AdminViewModel(application: Application) : BaseViewModel(application) {
.....
}
Теперь у вас всегда есть доступ к контексту, который вы можете использовать для получения доступа к ресурсам.
ViewModel
работает с чем-то более сложным, чемBitmap
, то у вашегоViewModel
неправильные обязанности. - person CommonsWare   schedule 14.06.2017ViewModel
должно вызыватьsetContacts()
вViewModel
.ViewModel
должен быть немного больше, чем POJO. - person CommonsWare   schedule 14.06.2017