How to check permission is granted in ViewModel?

前端 未结 3 1591
天涯浪人
天涯浪人 2021-02-08 19:53

I need to ask permission for contacts and when application starts I\'m asking,in ViewModel part I need to call method which requires permission. I need to check permission is gr

相关标签:
3条回答
  • 2021-02-08 20:17

    I just ran into this problem, and I decided to use make use of LiveData instead.

    Core concept:

    • ViewModel has a LiveData on what permission request needs to be made

    • ViewModel has a method (essentially callback) that returns if permission is granted or not

    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")
            })
            ......
        }
    }
    
    0 讨论(0)
  • 2021-02-08 20:18

    I have reworked the solution. The PermissionRequester object is everything you need to request permissions from any point where you have at least an application context. It uses its helper PermissionRequestActivity to accomplish this job.

    @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()
        }
    }
    

    Usage:

    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()
        }
    }
    
    0 讨论(0)
  • 2021-02-08 20:26

    I did something like this:

    create an abstract class that extends AndroidViewModel which gives you access to the application context:

    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()
        }
    }
    

    Now, create your view model by extending the BaseViewModel class and you will have access to the application context

    class AdminViewModel(application: Application) : BaseViewModel(application) {
        .....
    }
    

    Now you always have access to a Context that you can use to get access to resources.

    0 讨论(0)
提交回复
热议问题