基于kotlin的对于安卓6.0以上的动态权限申请
总逻辑为:
(1)启动界面判断是否拥有所需权限
(2)启动时先提示用户本app所需要的权限
(3)用户选择是否接受
(4)不接受将会一直循环权限申请界面
(5)全部权限获取后进入app
在开启app时启用一个PageStartActivity来对权限进行判断与申请
class PageStartActivity : AppCompatActivity() {
private var mActivity: Activity? = null
private var mPermApplyDialog: AlertDialog? = null
private var mPermSettingDialog: AlertDialog? = null
private var mShowViewDis: Disposable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
createView()
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
supportActionBar!!.hide()
}
private fun createView(){
verticalLayout {
lparams(matchParent, matchParent)
imageView(R.drawable.page_start){
scaleType = ImageView.ScaleType.FIT_XY
}
val al = alert{
icon = resources.getDrawable(R.mipmap.ic_launcher)
title = "温馨提示"
message = "使用该程序前,需要对手机的相机及储存权限进行申请"
isCancelable = false
neutralPressed("我知道了"){
initView()
}
}
if(Util.isPermitted(context,true)){
al.show()
}else{
initView()
}
}
}
private fun initView() {
mActivity = this
//动态申请权限
if (!Util.isPermissionsApply(this, true)) {
createShowViewSub()
}
}
private fun createShowViewSub() {
val fl = Flowable.timer(500, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
startActivity(Intent(mActivity, MainActivity::class.java))
finish()
}
mShowViewDis = fl.subscribe()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
PERM_SETTING_APPLY_CODE -> if (!Util.isPermissionsApply(this, false)) { //权限已经全部申请
if (mPermSettingDialog != null && mPermSettingDialog!!.isShowing()) {
mPermSettingDialog!!.dismiss()
}
createShowViewSub()
}
}
}
/**
* 注册权限申请回调
*
* @param requestCode 申请码
* @param permissions 申请的权限
* @param grantResults 结果
*/
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
when (requestCode) {
Config.PERMS_APPLY_CODE -> if (!Util.isPermissionOk(grantResults)) { //有未申请的权限
var isCanShowApplyAgain = false
for (i in permissions.indices) {
if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity!!, permissions[i])) { //只要有一个权限显示可以弹出继续申请,则可以弹出申请权限对话框
isCanShowApplyAgain = true
break
}
}
if (isCanShowApplyAgain) { //可以继续弹出权限申请
showPermApplyDialog()
} else { //不能够继续弹出
showPermSettingDialog()
}
} else {
createShowViewSub()
}
}
}
/**
* 有未申请的权限,未选择不再询问,继续弹出权限申请
*/
private fun showPermApplyDialog() {
mPermApplyDialog = AlertDialog.Builder(this)
.setTitle(R.string.perm_apply_title)
.setMessage(R.string.perm_apply_msg)
.setPositiveButton(R.string.apply) { dialogInterface, i -> Util.isPermissionsApply(mActivity!!, true) }
.setNegativeButton(R.string.cancel) { dialogInterface, i -> finish() }
.setCancelable(false)
.create()
mPermApplyDialog!!.show()
}
/**
* 有未申请的权限,并选了不再询问,进入设置设置应用权限
*/
private fun showPermSettingDialog() {
mPermSettingDialog = AlertDialog.Builder(this)
.setTitle(R.string.perm_apply_title)
.setMessage(R.string.perm_setting_msg)
.setPositiveButton(R.string.goto_setting, null)
.setNegativeButton(R.string.cancel) { dialogInterface, i -> finish() }
.setCancelable(false)
.create()
mPermSettingDialog!!.show()
//以下方式可以让alertDialog点击时不自动消失
mPermSettingDialog!!.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val intent = Intent()
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
intent.data = Uri.fromParts("package", packageName, null)
startActivityForResult(intent, PERM_SETTING_APPLY_CODE)
}
}
override fun onDestroy() {
super.onDestroy()
Util.disposeSubscribe(mShowViewDis!!)
}
companion object {
private val PERM_SETTING_APPLY_CODE = 10
}
}
util.kt中对于所需的权限进行判断并申请
object Util : ActivityCompat(){
fun isPermissionOk(grantResults: IntArray): Boolean {
var isOk = true
for (i in grantResults.indices) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { //含有未申请到的权限
isOk = false
break
}
}
return isOk
}
fun isPermitted(context: Context, isApplyPerm: Boolean): Boolean{
var isNeedApplyPerm = false
val unApplyedList = ArrayList()
for (i in 0 until PERMISSIONS.size) {
if (ContextCompat.checkSelfPermission(context, PERMISSIONS[i]) != PackageManager.PERMISSION_GRANTED) {
unApplyedList.add(PERMISSIONS[i]) //申请过的权限将其删除,不要再提示给用户
}
}
if (unApplyedList.size > 0) { //有未申请的权限,去申请
isNeedApplyPerm = true
}
return isNeedApplyPerm
}
//动态申请权限(Android6.0以上需要如此)
fun isPermissionsApply(context: Context, isApplyPerm: Boolean): Boolean {
var isNeedApplyPerm = false
//判断剩余还未申请的权限
val unApplyedList = ArrayList()
for (i in 0 until PERMISSIONS.size) {
if (ContextCompat.checkSelfPermission(context, PERMISSIONS[i]) != PackageManager.PERMISSION_GRANTED) {
unApplyedList.add(PERMISSIONS[i]) //申请过的权限将其删除,不要再提示给用户
}
}
if (unApplyedList.size > 0) { //有未申请的权限,去申请
isNeedApplyPerm = true
if (isApplyPerm) {
val unApplyedArray:Array = unApplyedList.toTypedArray()
ActivityCompat.requestPermissions(context as Activity, unApplyedArray,PERMS_APPLY_CODE)
}
}
return isNeedApplyPerm
}
//解注册观察者模式
fun disposeSubscribe(vararg disposables: Disposable) {
for (disposable in disposables) {
if (disposable != null && !disposable.isDisposed) {
disposable.dispose()
}
}
}
}
一个用来存放权限的对象
object Config {
//权限code
val PERMS_APPLY_CODE = 0
//需要申请的权限列表
var PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
}
当然还有一个什么都没有的主界面
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
由于引入了anko和rerxjava2的框架,不要忘记在build.gradle:app中添加实现
implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
//Anko Version
ext.anko_version='0.10.4'
// Anko Commons
implementation "org.jetbrains.anko:anko-commons:$anko_version"
// Anko Layouts
implementation "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// Coroutine listeners for Anko Layouts
implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
// Anko SQLite
implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
//anko 拓展
implementation "org.jetbrains.anko:anko-sdk25:$anko_version"
implementation "org.jetbrains.anko:anko-recyclerview-v7:$anko_version"
虽然有些用不到,但是每次都复制习惯了
一路下来参考了很多的代码,其中帮助最大的是GitHub上的这份点击打开链接
虽然我心目中想实现的是qq音乐启动时的那种样式,但是对于图形界面如何绘制还是不太了解,最终没能达到那种视觉效果,而且该方法还存在一个问题:如果qq音乐在启动时强行修改权限就会导致应用重启,从而在下次登录时重新检测权限以免出现意外,而我这段如果未关闭后台则会依然运行,有可能产生错误。
当然小小吐槽一句,网易云既不会在修改权限时重启,而且就算关闭后台也不进行二次检测,我就这样关闭了存储器读取权限,导致本地音乐无法读取却不做任何提示。
anko中对于dialog的个性化定制还是没有弄清楚,就只好采取了默认样式
源码在https://github.com/thirstysheep/thirstysheep-permission