Android6.0运行时权限原生实现和MIUI下的处理

2023-12-13 06:08

本文主要是介绍Android6.0运行时权限原生实现和MIUI下的处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、前言

自从Android6.0发布,增加了许多新的特性和功能,除了强化和完善了MD设计元素,Android的安全也得到了谷歌的重视。于是,Android6.0中出现了运行时权限的概念。许多程序员前赴后继,推出了大量的优秀的第三方库,来简化运行时权限的使用。但是,我觉得我们有必要从根本上学会使用运行时权限的申请,这能增加我们对Permission的理解。
本教程代码:https://github.com/sendtion/SDCPermission


2、运行时权限

当我们的targetSdkVersion>=23时,我们一定要注意权限的申请,否则App会因为没有获取权限而发生崩溃。当然,在API 23以下系统版本也可以判断一下权限是否得到,确保万无一失。
我们不能连续一次性申请多个权限,这会使用户感觉到不安全,感觉App有窃取隐私信息的可能,可能因此丧失一部分用户。也不能在App内到处申请权限,这会是用户感到烦躁和麻烦,这也会丧失一部分用户。

所以,我们申请权限的时候一定要注意一些原则:

a>尽量精简权限,剔除不必要的权限
大量的权限,只会让用户丧失安全感或者感到烦躁,得不偿失。

b>用户拒绝后再次请求一定要详细说明缘由
给用户详细说明,我们需要这个权限来干什么,用户才更容易接受。

c>在合理的位置申请权限
不要一次性申请所有的权限,这中感觉很不好,会让用户感觉自己被强X了。


3、系统权限分类

Google将权限分为两类,一类是普通权限(Normal Permissions),这类权限一般不涉及用户隐私,也不需要用户进行授权,如网络访问,手机震动等,这些权限如下所示:

ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS

另外一类是危险权限(Dangerous Permission),涉及用户隐私,需要用户授权,如对sd卡读取、访问用户手机通讯录等。如下所示:

android.permission-group.CALENDAR(日历数据) 
android.permission.READ_CALENDAR
android.permission.WRITE_CALENDARandroid.permission-group.CAMERA(相机) 
android.permission.CAMERAandroid.permission-group.CONTACTS(联系人)  
android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTSandroid.permission-group.LOCATION(位置)   
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATIONandroid.permission-group.MICROPHONE(麦克风)    
android.permission.RECORD_AUDIOandroid.permission-group.PHONE(电话)  
android.permission.READ_PHONE_STATE
android.permission.CALL_PHONE
android.permission.READ_CALL_LOG
android.permission.WRITE_CALL_LOG
com.android.voicemail.permission.ADD_VOICEMAIL
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLSandroid.permission-group.SENSORS(传感器)   
android.permission.BODY_SENSORSandroid.permission-group.SMS(短信)    
android.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
android.permission.READ_CELL_BROADCASTSandroid.permission-group.STORAGE(存储)    
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE

查看Dangerous Permission可以发现权限是分组的,这个和Android 6.0的授权机制有关。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。

注:不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。


4、权限申请方式

以请求读取联系人权限为例:
a> 在AndroidManifest.xml文件中声明所需要的权限
b> 检查是否授予了所需要的权限

ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED

ContextCompat.checkSelfPermission()主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。

c> 如果没有授予权限,则请求授权

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS},code);

该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。

d> 处理权限申请的结果

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//TODOif(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获得了权限} else {//拒绝了权限}
}

e> 如果用户拒绝了,下次申请给予解释

ActivityCompat.shouldShowRequestPermissionRationale(this, permission)

这个API主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。

注:
* 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
* 如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。
* 如果设备规范禁止应用具有该权限,此方法也会返回 false。


5、优化

到这里算是处理完了,这么看起来并没有多麻烦,但是坑还是存在的。Android碎片化严重,大家都知道。国产的各种定制系统深度修改,好多底层API都有所改变,给我们Android开发者带来了极大的麻烦。诸如MIUI、Flyme、EMUI、FuntouchOS、ColorOS、SmartisanOS、CoolUI、H2OS等等等,各种各样,五花八门。

本教程的例子用的是小米5 MIUI8 7.0开发版,就遇到了很坑的事情。
小米默认是允许读写存储卡权限的,其他的权限都是询问状态。
这里写图片描述

经测试,
默认询问,除了日历、录音、相机、存储卡会弹窗,你可以选择拒绝或者允许;其他权限不弹窗,checkSelfPermission直接返回0,也就是直接允许了,此时权限状态仍然为询问状态;
手动拒绝后不会再弹窗,相当于选中了不再询问,权限状态由询问变为拒绝,需要手动给予权限;
手动允许后,权限状态由询问变为允许;
MIUI上:shouldShowRequestPermissionRationale()返回一直是false,无法进行判断解释。

GitHub地址:https://github.com/sendtion/SDCPermission
完整的代码如下:

package com.sdc.sdcpermission;import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;/*** Android 6.0原生系统运行时权限适配* 在联想乐檬X3测试通过*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private static final String TAG = "MainActivity";//快捷键logtprivate TextView tv_sdcard;private TextView tv_sms;private TextView tv_phone;private TextView tv_contacts;private TextView tv_location;private TextView tv_record;private TextView tv_camera;private TextView tv_calender;private TextView tv_sensor;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initViews();}private void initViews() {Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);tv_contacts = (TextView) findViewById(R.id.tv_contacts);tv_sms = (TextView) findViewById(R.id.tv_sms);tv_phone = (TextView) findViewById(R.id.tv_phone);tv_location = (TextView) findViewById(R.id.tv_location);tv_sdcard = (TextView) findViewById(R.id.tv_sdcard);tv_record = (TextView) findViewById(R.id.tv_record);tv_calender = (TextView) findViewById(R.id.tv_calender);tv_camera = (TextView) findViewById(R.id.tv_camera);tv_sensor = (TextView) findViewById(R.id.tv_sensor);tv_contacts.setOnClickListener(this);tv_sms.setOnClickListener(this);tv_phone.setOnClickListener(this);tv_location.setOnClickListener(this);tv_sdcard.setOnClickListener(this);tv_record.setOnClickListener(this);tv_calender.setOnClickListener(this);tv_camera.setOnClickListener(this);tv_sensor.setOnClickListener(this);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.menu_main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {int id = item.getItemId();if (id == R.id.action_miui) {//跳转到MIUI的权限适配界面Intent intent = new Intent();intent.setClass(this, MIUIActivity.class);startActivity(intent);return true;}return super.onOptionsItemSelected(item);}private void showToast(String text){Toast.makeText(this, text, Toast.LENGTH_SHORT).show();}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.tv_contacts://READ_CONTACTS是读取联系人if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限//如果没有权限则申请权限showTipsDialog("读取联系人", Manifest.permission.READ_CONTACTS, 101);} else {showToast("已经获得‘读取联系人’权限");}break;case R.id.tv_location://ACCESS_FINE_LOCATION是GPS定位,ACCESS_COARSE_LOCATION是网络定位if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("GPS定位", Manifest.permission.ACCESS_FINE_LOCATION, 102);} else {showToast("已经获得‘GPS定位’权限");}break;case R.id.tv_sdcard://READ_EXTERNAL_STORAGE是读取存储卡,if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("读存储卡", Manifest.permission.READ_EXTERNAL_STORAGE, 103);} else {showToast("已经获得‘读存储卡’权限");}break;case R.id.tv_sms://READ_SMS是读取短信,SEND_SMS是发送短信if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("读取短信", Manifest.permission.READ_SMS, 104);} else {showToast("已经获得‘读取短信’权限");}break;case R.id.tv_phone://CALL_PHONE是拨打电话if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("拨打电话", Manifest.permission.CALL_PHONE, 105);} else {showToast("已经获得‘拨打电话’权限");}break;case R.id.tv_record://RECORD_AUDIO是录音if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("录音", Manifest.permission.RECORD_AUDIO, 106);} else {showToast("已经获得‘录音’权限");}break;case R.id.tv_camera://CAMERA是调用相机if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("调用相机", Manifest.permission.CAMERA, 107);} else {showToast("已经获得‘调用相机’权限");}break;case R.id.tv_calender://WRITE_CALENDAR是写日历if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("写日历", Manifest.permission.WRITE_CALENDAR, 108);} else {showToast("已经获得‘写日历’权限");}break;case R.id.tv_sensor://BODY_SENSORS是传感器权限if (ContextCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS) !=PackageManager.PERMISSION_GRANTED){//拒绝了权限,或者没有获得权限showTipsDialog("传感器", Manifest.permission.BODY_SENSORS, 109);} else {showToast("已经获得‘传感器’权限");}break;}}/*** ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_CONTACTS)* 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。* 如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。* 如果设备规范禁止应用具有该权限,此方法也会返回 false。* @param requestCode* @param permissions* @param grantResults*/@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode){case 101:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){showToast("已经获得‘读取联系人’权限???");} else {//拒绝了权限showToast("拒绝获得‘读取联系人’权限");getAppDetailSettingIntent("读取联系人");}break;case 102:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘GPS定位’权限???");} else {//拒绝了权限showToast("拒绝获得‘GPS定位’权限");getAppDetailSettingIntent("GPS定位");}break;case 103:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘读存储卡’权限???");} else {//拒绝了权限showToast("拒绝获得‘读存储卡’权限");getAppDetailSettingIntent("读存储卡");}break;case 104:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘读取短信’权限???");} else {//拒绝了权限showToast("拒绝获得‘读取短信’权限");getAppDetailSettingIntent("读取短信");}break;case 105:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘拨打电话’权限???");} else {//拒绝了权限showToast("拒绝获得‘拨打电话’权限");getAppDetailSettingIntent("拨打电话");}break;case 106://录音默认询问,请求权限会弹窗if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘录音’权限???");} else {//拒绝了权限showToast("拒绝获得‘录音’权限");getAppDetailSettingIntent("录音");}break;case 107://相机默认询问,请求权限会弹窗if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘调用相机’权限???");} else {//拒绝了权限showToast("拒绝获得‘调用相机’权限");getAppDetailSettingIntent("调用相机");}break;case 108://日历默认询问,请求权限会弹窗if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘写日历’权限???");} else {//拒绝了权限showToast("拒绝获得‘写日历’权限");getAppDetailSettingIntent("写日历");}break;case 109://传感器默认允许,不会弹窗if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){//获取了权限showToast("已经获得‘传感器’权限???");} else {//拒绝了权限showToast("拒绝获得‘传感器’权限");getAppDetailSettingIntent("传感器");}break;}}/*** 显示对话框,提示用户允许权限* @param name*/public void showTipsDialog(String name, final String permission, final int code){if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("权限申请提示");builder.setMessage("当前应用缺少"+name+"权限。是否立即申请权限?");builder.setNegativeButton("取消", null);builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission},code);}});builder.create().show();} else {showToast("我们需要"+name+"权限,给我吧!");//拒绝后不再询问选中的话,下面就不会在执行//如果选中不再询问,则询问状态会变成拒绝,否则一直是询问状态ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission},code);}}/*** 跳转到App详情页*/public void getAppDetailSettingIntent(String name) {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("权限申请提示");builder.setMessage("当前应用缺少"+name+"权限。请到应用信息——>权限管理中手动给予权限。");builder.setNegativeButton("取消", null);builder.setPositiveButton("设置", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {Intent localIntent = new Intent();localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);if (Build.VERSION.SDK_INT >= 9) {localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");localIntent.setData(Uri.fromParts("package", getPackageName(), null));} else if (Build.VERSION.SDK_INT <= 8) {localIntent.setAction(Intent.ACTION_VIEW);localIntent.setClassName("com.android.settings","com.android.settings.InstalledAppDetails");localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());}startActivity(localIntent);}});builder.create().show();}}

6、第三方库推荐

第三方库推荐,列举的有点多,至于你喜欢哪个,需要自己去发现~
前三个都不错,个人用的第四个!

yanzhenjie/AndPermission: Android permission.
https://github.com/yanzhenjie/AndPermission (严振杰)
hongyangAndroid/MPermissions: a easy API to use runtime permission for Android M
https://github.com/hongyangAndroid/MPermissions (鸿洋)
lovedise/PermissionGen: Android API easy to use permission for Android M
https://github.com/lovedise/PermissionGen
tbruyelle/RxPermissions: Android runtime permissions powered by RxJava 支持RxJava的动态权限
https://github.com/tbruyelle/RxPermissions
forJrking/HeiPermission: Hei 一句代码搞定 Android M 动态权限检测。信不信由你,反正我用了。
https://github.com/forJrking/HeiPermission
rebus007/PermissionUtils: Check marshmallow permission easily
https://github.com/rebus007/PermissionUtils
mylhyl/AndroidAcp: 一句话搞定,简化Android 6.0 系统复杂的权限操作
https://github.com/mylhyl/AndroidAcp
lypeer/FcPermissions: Fuck the permissions in Android M
https://github.com/lypeer/FcPermissions
Airsaid/MPermissionUtils: Android6.0 运行时权限 超轻量级工具类
https://github.com/Airsaid/MPermissionUtils
AndSync/XPermissionUtils: 可能是最精简的Android6.0运行时权限处理方式
https://github.com/AndSync/XPermissionUtils

如有疑问,欢迎指正!

个人简介:
GitHub:https://github.com/sendtion
博客:http://sendtion.cn/
简书:http://www.jianshu.com/users/630d98ed1424

这篇关于Android6.0运行时权限原生实现和MIUI下的处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/487369

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P