调用原生方法(以跳转到设置页面设置是否开启通知权限为例):
首先在flutter项目的android下创建一个NotificationSetUtil(kotlin)工具类
首先判断是否开启了通知权限,可以通过NotificationManagerCompat 中的 areNotificationsEnabled()来判断是否开启通知权限
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
fun isNotificationEnabled(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val notificationManagerCompat = NotificationManagerCompat.from(context)
return notificationManagerCompat.areNotificationsEnabled()
}
val CHECK_OP_NO_THROW ="checkOpNoThrow"
val OP_POST_NOTIFICATION ="OP_POST_NOTIFICATION"
val mAppOps = context.getSystemService(Context.APP_OPS_SERVICE)as AppOpsManager
val appInfo = context.applicationInfo
val pkg = context.applicationContext.packageName
val uid = appInfo.uid
var appOpsClass: Class<*>? =null
/* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager::class.java.name)
val checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE,
String::class.java)
val opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION)
val value = opPostNotificationValue[Int::class.java]as Int
return checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg)as Int == AppOpsManager.MODE_ALLOWED
}catch (e: Exception) {
e.printStackTrace()
}
return false
}
再生成一个跳转到设置页面开启通知权限的方法(Android 8.0,Android 5.0-7.0,和Android 5.0以下的区分设置):
fun gotoSet(context: Context) {
val intent = Intent()
if (Build.VERSION.SDK_INT >=26) {
// android 8.0引导
intent.action ="android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName)
}else if (Build.VERSION.SDK_INT >=21) {
// android 5.0-7.0
intent.action ="android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("app_package", context.packageName)
intent.putExtra("app_uid", context.applicationInfo.uid)
}else {
// 其他
intent.action ="android.settings.APPLICATION_DETAILS_SETTINGS"
intent.data = Uri.fromParts("package", context.packageName, null)
}
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}
再做一个返回结果的监听:
interface OnNextLitener {
fun onNext()
fun hasPermission(hasPremission: Boolean)
}
生成判断是否需要打开设置打开通知权限:
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
fun OpenNotificationSetting(context: Context, mOnNextLitener: OnNextLitener?) {
if (!isNotificationEnabled(context)) {
mOnNextLitener?.hasPermission(false)
// gotoSet(context)
}else {
mOnNextLitener?.onNext()
}
}
至此,这个工具类就完成了(该类的完整代码会在文章最后贴出)。
在相同目录下,生成插件类NotificationtProviderPlugin
首先声明一个Channel名称(要注意这个ChannelName)
private const val ChannelName ="com.example.app_nh.plugins/notification"
注册通知插件(要注意when方法内的methodCall.method的两个值:getNotification、gotoset):
@JvmStatic
fun register(context: Context, messenger: BinaryMessenger) = MethodChannel(messenger, ChannelName).setMethodCallHandler{ methodCall, result->
when (methodCall.method) {
"getNotification" ->if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//判断是否需要开启通知栏功能
NotificationSetUtil.OpenNotificationSetting(context, object : NotificationSetUtil.OnNextLitener {
override fun onNext() {
result.success(true)
}
override fun hasPermission(hasPremission: Boolean) {
result.success(false)
}
})
}
"gotoset" -> NotificationSetUtil.gotoSet(context)
}
result.success(null)//没有返回值,所以直接返回为null
}
到这一步,关于Android 设置开启通知权限的代码就完成了,但是还需要在MainActivity文件中注册一次,如下:
NotificationtProviderPlugin.register(this, flutterEngine.dartExecutor.binaryMessenger)
到这里,整个插件开发完成了,剩下的就是需要在flutter代码中调用:
首先要声明一个MethodChannel(下方代码括号中的名称要和上述Android 代码中的ChannelName相匹配,才能达到一对一的关系):
static const _platform = const MethodChannel('com.example.app_nh.plugins/notification');
判断是否有通知权限:
var hasPermission = await _platform.invokeMethod('getNotification');
没有通知权限就弹窗提醒是否跳转至设置页面开启:
if (!hasPermission) {
showDialog(
context:context,
barrierDismissible:true,
builder: (BuildContext context) {
return SetPermissionDialog(
text:"需要开启通知权限,是否前往设置页面开启",
onPressed: () {
Navigator.of(context).pop();
_platform.invokeMethod('gotoset');
},
);
});
}
上面代码中_platform.invokeMethod()括号中的字段同样要和上述Android 代码中methodCall.method的两个值一一对应来完成Android 方法的调用。
下面附上NotificationtProviderPlugin类和NotificationSetUtil类的完整代码:
NotificationSetUtil:
import android.app.AppOpsManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationManagerCompat
class NotificationSetUtil {
interface OnNextLitener {
fun onNext()
fun hasPermission(hasPremission: Boolean)
}
private var mOnNextLitener: OnNextLitener? =null
fun setOnNextLitener(mOnNextLitener: OnNextLitener?) {
this.mOnNextLitener = mOnNextLitener
}
companion object {
//判断是否需要打开设置界面
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
fun OpenNotificationSetting(context: Context, mOnNextLitener: OnNextLitener?) {
if (!isNotificationEnabled(context)) {
mOnNextLitener?.hasPermission(false)
// gotoSet(context)
}else {
mOnNextLitener?.onNext()
}
}
//判断该app是否打开了通知
/**
* 可以通过NotificationManagerCompat 中的 areNotificationsEnabled()来判断是否开启通知权限。
* API 在19以下 一直是true
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
fun isNotificationEnabled(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val notificationManagerCompat = NotificationManagerCompat.from(context)
return notificationManagerCompat.areNotificationsEnabled()
}
val CHECK_OP_NO_THROW ="checkOpNoThrow"
val OP_POST_NOTIFICATION ="OP_POST_NOTIFICATION"
val mAppOps = context.getSystemService(Context.APP_OPS_SERVICE)as AppOpsManager
val appInfo = context.applicationInfo
val pkg = context.applicationContext.packageName
val uid = appInfo.uid
var appOpsClass: Class<*>? =null
/* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager::class.java.name)
val checkOpNoThrowMethod =appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String::class.java)
val opPostNotificationValue =appOpsClass.getDeclaredField(OP_POST_NOTIFICATION)
val value = opPostNotificationValue[Int::class.java]as Int
return checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg)as Int == AppOpsManager.MODE_ALLOWED
}catch (e: Exception) {
e.printStackTrace()
}
return false
}
//打开手机设置页面
fun gotoSet(context: Context) {
val intent = Intent()
if (Build.VERSION.SDK_INT >=26) {
// android 8.0引导
intent.action ="android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName)
}else if (Build.VERSION.SDK_INT >=21) {
// android 5.0-7.0
intent.action ="android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("app_package", context.packageName)
intent.putExtra("app_uid", context.applicationInfo.uid)
}else {
// 其他
intent.action ="android.settings.APPLICATION_DETAILS_SETTINGS"
intent.data = Uri.fromParts("package", context.packageName, null)
}
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}
}
}
NotificationtProviderPlugin:
import android.content.Context
import android.os.Build
import com.example.app_nh.NotificationSetUtil
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
object NotificationtProviderPlugin {
/** Channel名称 **/
private const val ChannelName ="com.example.app_nh.plugins/notification"
/**
* 注册通知插件
*/
@JvmStatic
fun register(context: Context, messenger: BinaryMessenger) = MethodChannel(messenger, ChannelName).setMethodCallHandler{ methodCall, result->
when (methodCall.method) {
"getNotification" ->if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//判断是否需要开启通知栏功能
NotificationSetUtil.OpenNotificationSetting(context, object : NotificationSetUtil.OnNextLitener {
override fun onNext() {
result.success(true)
}
override fun hasPermission(hasPremission: Boolean) {
result.success(false)
}
})
}
"gotoset" -> NotificationSetUtil.gotoSet(context)
}
result.success(null)//没有返回值,所以直接返回为null
}
}