Android 设置铃声和闹钟
Android设置铃声和闹钟使用的方法是一样的,但是要区别的去获取对应的权限。
统一权限,不管是设置闹钟还是铃声,他们都需要一个系统设置权限如下:
//高版本需要WRITE_SETTINGS权限//此权限是敏感权限,无法动态申请,需要跳转到系统界面开启if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//判断是否已经开启权限if (!Settings.System.canWrite(mContext)) {//没有开启这里需要一个弹窗来提醒用户要去设置下这个权限//自己的demo可以忽略此步骤,应用商店权限申请前需要说明mBindView.tvTitle.post {//这是我自己的权限说明弹窗,自己的定义即可OpenWriteDialog.show(this.supportFragmentManager){if (it){//这一步是跳转到系统设置界面,跳转之后有个回调,判断是否已经开启,开启了继续处理下一步val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)intent.data = Uri.parse("package:$packageName")startActivityForResult(intent, PERMISSION_LOCAL_CODE)}}}} else {//开启了运行下一步todo()}} else {//低版本直接运行下一步todo()}
@RequiresApi(Build.VERSION_CODES.M)override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)//回调判断code是否一致if (requestCode == PERMISSION_LOCAL_CODE){//判断是否已经开启if (Settings.System.canWrite(this)) {//开启了进行下一步todo()}}}
设置铃声或者闹钟前都要先进行上一步的权限判断才可以继续进行
设置闹钟
首先动态判断权限
Manifest.permission.SET_ALARM
Manifest.permission.READ_EXTERNAL_STORAGE
Manifest.permission.WRITE_EXTERNAL_STORAGE
然后调用代码即可
RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_ALARM, uri)
注意这里的uri是手机本地的铃声路径,大多数的需求是下载网络.mp3 铃声到本地,然后更新闹钟,这里注意下下载之后需要更新到媒体库才可以正常设置,否则设置出来可能是未知或者直接设置不成功----------如何下载更新到媒体库后面统一讲
设置铃声
同闹钟一样,首先需要动态获取权限
Manifest.permission.READ_EXTERNAL_STORAGE
Manifest.permission.WRITE_EXTERNAL_STORAGE
其次设置铃声即可
RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE, uri)
这里的uri同闹钟一样,需要更新到媒体库才可以设置
下载网页铃声到本地
DownloadUtil.download("网页链接","存储路径", "文件名称.mp3",object : DownloadUtil.OnDownloadListener{override fun onDownloadSuccess(file: File?) {//下载成功以及下载后的文件}override fun onDownloading(progress: Int) {//下载进度}override fun onDownloadFailed(e: Exception?) {//下载失败}})
存储路径地址这里给出建议写法
private fun getUrlPath(): String {val externalFilesDir: File? = this.getExternalFilesDir("")val customFile = File(externalFilesDir!!.absolutePath, "Sandbox")if (!customFile.exists()) {customFile.mkdirs()}return customFile.absolutePath + File.separator}
文件名称后缀 .mp3即可
DownloadUtil源码
object DownloadUtil {private var okHttpClient: OkHttpClient? = null/*** @param url 下载连接* @param destFileDir 下载的文件储存目录* @param destFileName 下载文件名称* @param listener 下载监听*/fun download(url: String, destFileDir: String, destFileName: String, listener: OnDownloadListener) {if (url == null || url == ""){return}if (okHttpClient == null){okHttpClient = OkHttpClient()}val request: Request = Request.Builder().url(url).build()okHttpClient!!.newCall(request).enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {// 下载失败监听回调listener.onDownloadFailed(e)}@Throws(IOException::class)override fun onResponse(call: Call, response: Response) {if (response.body != null) {var inputStream: InputStream? = nullval buf = ByteArray(2048)var len = 0var fos: FileOutputStream? = null// 储存下载文件的目录val dir = File(destFileDir)if (!dir.exists()) {dir.mkdirs()}val file = File(dir, destFileName)try {inputStream = response.body!!.byteStream()val total: Long = response.body!!.contentLength()fos = FileOutputStream(file)var sum: Long = 0while (inputStream.read(buf).also { len = it } != -1) {fos.write(buf, 0, len)sum += len.toLong()val progress = (sum * 1.0f / total * 100).toInt()// 下载中更新进度条listener.onDownloading(progress)}fos.flush()// 下载完成listener.onDownloadSuccess(file)} catch (e: Exception) {listener.onDownloadFailed(e)} finally {try {inputStream?.close()} catch (e: IOException) {listener.onDownloadFailed(e)}try {fos?.close()} catch (e: IOException) {listener.onDownloadFailed(e)}}}else{listener.onDownloadFailed(IOException("接口失败"))}}})}interface OnDownloadListener {/*** @param file 下载成功后的文件*/fun onDownloadSuccess(file: File?)/*** @param progress 下载进度*/fun onDownloading(progress: Int)/*** @param e 下载异常信息*/fun onDownloadFailed(e: Exception?)}}
此时就将网络音频下载到本地了,这时候拿到file是无法更新到闹钟或者铃声的,甚至本地音乐里面都找不它,需要更新到媒体库才可以进行设置
更新到媒体库
更新媒体库使用的是 ContentValues 以前文章写过下载视频到本地,都是一样的,只不过参数不同
可以看下参数对比下
更新到媒体库的时候要注意 高版本和低版本区分更新
如果你此时使用的是网上大多数的 MediaScannerConnection.scanFile() 方法,大概率是不会成功的
使用
DangUtils.setMYRingtone(mContext,mDownFile!!.absolutePath,mDownType,mDownName);
其中 mDownFile 就是我下载到本地的文件
DangUtils 代码
object DangUtils {/*** 将资源更新到媒体库* context - 上下文* filePath - 本地路径* type - 类型-设置闹钟还是铃声* name - 名称,提示用户的 可有可无*/fun setMYRingtone(context: Context, filePath: String?,type: String,name: String): Boolean {if (filePath == null || filePath == ""){return false}return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {saveVideoToAlbumBeforeQ(context, filePath,type,name)} else {saveVideoToAlbumAfterQ(context, filePath,type,name)}}private fun saveVideoToAlbumAfterQ(context: Context, filePath: String,mDownType: String,name: String): Boolean {return try {val contentResolver = context.contentResolverval tempFile = File(filePath)val contentValues = getVideoContentValues(context, tempFile, System.currentTimeMillis())val uri = contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues)copyFileAfterQ(context, contentResolver, tempFile, uri)contentValues.clear()contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)context.contentResolver.update(uri!!, contentValues, null, null)context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))if (mDownType == Setting_LS) {// 设置系统来电铃声RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE, uri)Toast.makeText(context, "已将${name}设置为来电铃声", Toast.LENGTH_SHORT).show()}else{RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_ALARM, uri)Toast.makeText(context, "已将${name}设置为闹铃", Toast.LENGTH_SHORT).show()}true} catch (e: java.lang.Exception) {e.printStackTrace()if (mDownType == Setting_LS) {Toast.makeText(context, "铃声设置失败", Toast.LENGTH_SHORT).show()}else{Toast.makeText(context, "闹铃设置失败", Toast.LENGTH_SHORT).show()}false}}private fun saveVideoToAlbumBeforeQ(context: Context, videoFile: String,mDownType: String,name: String): Boolean {val picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)val tempFile = File(videoFile)val destFile = File(picDir, context.packageName + File.separator + tempFile.name)var ins: FileInputStream? = nullvar ous: BufferedOutputStream? = nullreturn try {ins = FileInputStream(tempFile)ous = BufferedOutputStream(FileOutputStream(destFile))var nread = 0Lval buf = ByteArray(1024)var n: Intwhile (ins.read(buf).also { n = it } > 0) {ous.write(buf, 0, n)nread += n.toLong()}MediaScannerConnection.scanFile(context, arrayOf(destFile.absolutePath),null) { path: String?, uri: Uri? ->if (mDownType == Setting_LS) {// 设置系统来电铃声RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE, uri)Toast.makeText(context, "已将${name}设置为来电铃声", Toast.LENGTH_SHORT).show()}else{RingtoneManager.setActualDefaultRingtoneUri(context, RingtoneManager.TYPE_ALARM, uri)Toast.makeText(context, "已将${name}设置为闹铃", Toast.LENGTH_SHORT).show()}}true} catch (e: java.lang.Exception) {e.printStackTrace()if (mDownType == Setting_LS) {Toast.makeText(context, "铃声设置失败", Toast.LENGTH_SHORT).show()}else{Toast.makeText(context, "闹铃设置失败", Toast.LENGTH_SHORT).show()}false} finally {try {ins?.close()ous?.close()} catch (e: IOException) {e.printStackTrace()}}}@Throws(IOException::class)private fun copyFileAfterQ(context: Context,localContentResolver: ContentResolver,tempFile: File,localUri: Uri?) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&context.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {//拷贝文件到相册的uri,android10及以上得这么干,否则不会显示。可以参考ScreenMediaRecorder的save方法val os = localContentResolver.openOutputStream(localUri!!)Files.copy(tempFile.toPath(), os)os!!.close()tempFile.delete()}}/*** 获取视频的contentValue*/private fun getVideoContentValues(context: Context, paramFile: File, timestamp: Long): ContentValues {val localContentValues = ContentValues()if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {localContentValues.put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_MUSIC+ File.separator + context.packageName)}localContentValues.put(MediaStore.Audio.Media.TITLE, paramFile.name)localContentValues.put(MediaStore.Audio.Media.DISPLAY_NAME, paramFile.name)localContentValues.put(MediaStore.Audio.Media.MIME_TYPE, "music/mp3")localContentValues.put(MediaStore.Audio.Media.DATE_TAKEN, timestamp)localContentValues.put(MediaStore.Audio.Media.DATE_MODIFIED, timestamp)localContentValues.put(MediaStore.Audio.Media.DATE_ADDED, timestamp)localContentValues.put(MediaStore.Audio.Media.SIZE, paramFile.length())return localContentValues}}
相关文章:

Android 设置铃声和闹钟
Android设置铃声和闹钟使用的方法是一样的,但是要区别的去获取对应的权限。 统一权限,不管是设置闹钟还是铃声,他们都需要一个系统设置权限如下: //高版本需要WRITE_SETTINGS权限//此权限是敏感权限,无法动态申请,需要…...

自动化测试模型(一)
8.8.1 自动化测试模型概述 在自动化测试运用于测试工作的过程中,测试人员根据不同自动化测试工具、测试框架等所进行的测试活动进行了抽象,总结出线性测试、模块化驱动测试、数据驱动测试和关键字驱动测试这4种自动化测试模型。 线性测试 首先&#…...

解决nuxt3下载慢下载报错问题
在下载nuxt3时总是下不下来,最后还报错了。即使改成国内镜像源也不行。 解决方法: 直接去github上下载 https://github.com/nuxt/starter/tree/v3 解压后得到如下目录: 手动修改项目名和文件夹名 安装依赖 npm install可能会比较慢或下不…...
Ubuntu修改swap大小
查看swap位置和大小: swapon -s 方案一:修改原有文件大小方式 第一步:进入系统根目录cd /; 第二步:执行:sudo dd if/dev/zero of/swap bs1M count16384 //每段块1M 共16384块,即16G 第三步:执行…...

[C#] 复数乘法的跨平台SIMD硬件加速向量算法(不仅支持X86的Sse、Avx、Avx512,还支持Arm的AdvSimd)
文章目录 一、简单算法二、向量算法2.1 算法思路2.1.1 复数乘法的数学定义2.1.2 复数的数据布局2.1.3 第1步:计算 (a*c) (-b*d)i2.1.4 第2步:计算 (a*d) (b*c)i2.1.5 第3步:计算结果合并 2.2 算法实现(UseVectors)2.…...

C#WPF基础介绍/第一个WPF程序
什么是WPF WPF(Windows Presentation Foundation)是微软公司推出的一种用于创建窗口应用程序的界面框架。它是.NET Framework的一部分,提供了一套先进的用户界面设计工具和功能,可以实现丰富的图形、动画和多媒体效果。 WPF 使用…...

强大的接口测试可视化工具:Postman Flows
Postman Flows是一种接口测试可视化工具,可以使用流的形式在Postman工作台将请求接口、数据处理和创建实际流程整合到一起。如下图所示 Postman Flows是以API为中心的可视化应用程序开发界面。它提供了一个无限的画布用于编排和串连API,数据可视化来显示…...
系统设计及解决方案
发送验证码 1:根据手机号从Redis中获取value(验证码_时间戳) 2:如果value不为空,并且时间戳与当前时间戳的间隔小于60秒,则返回一个错误信息 3:生成随机验证码 4:调用阿里云短信服务API给用户发送短信验证码…...

从0入门自主空中机器人-2-2【无人机硬件选型-PX4篇】
1. 常用资料以及官方网站 无人机飞控PX4用户使用手册(无人机基本设置、地面站使用教程、软硬件搭建等):https://docs.px4.io/main/en/ PX4固件开源地址:https://github.com/PX4/PX4-Autopilot 飞控硬件、数传模块、GPS、分电板等…...

Linux之ARM(MX6U)裸机篇----2.汇编LED驱动实验
一,alpha的LED灯硬件原理分析 STM32 IO初始化流程 ①,使能GPIO时钟 ②,设置IO复用,复用为GPIO ③,配置GPIO的电气属性推挽,上拉下拉 ④,使用GPIO,输出高/低电平 MX6ULL IO初始化…...

e3 1220lv3 cpu-z分数
e3 1220lv3 双核四线程,1.1G频率,最低可在800MHZ运行,TDP 13W。 使用PE启动后测试cpu-z分数。 现在e3 1220lv3的价格落到69元。...
HTML5适配手机
要使 HTML5 网站适配手机设备,您可以遵循以下几个步骤和最佳实践: 1. 使用视口(Viewport) 在 HTML 文档的 <head> 部分添加视口元标签,以确保页面在移动设备上正确缩放和显示: <meta name"…...

C# 中使用 MassTransit
在生产环境中使用 MassTransit 时,通常需要进行详细的配置,包括设置连接字符串、配置队列、配置消费者、处理重试和错误队列等。以下是一个完整的示例,展示了如何在 ASP.NET Core 应用程序中配置 MassTransit,包括请求/响应模式和…...
网络编程 实现联网 b+Tree
网络编程是客户端和服务器之间通信的基础,也是现代应用开发中不可或缺的技能。在 Unity 中实现网络功能,需要结合计算机网络原理、数据结构与算法,以及网络协议的实际应用。以下是对这一块内容的详细介绍,包括每个涉及到的知识点&…...

zentao ubuntu上安装
#下载ZenTaoPMS-21.2-zbox_amd64.tar.gz(https://www.zentao.net/downloads.html) https://dl.zentao.net/zentao/21.2/ZenTaoPMS-21.2-zbox_amd64.tar.gzcd /opt tar -zxvf ZenTaoPMS-21.2-zbox_amd64.tar.gz#启动 /opt/zbox/zbox start /opt/zbox/zbox…...

Java 网络原理 ①-IO多路复用 || 自定义协议 || XML || JSON
这里是Themberfue 在学习完简单的网络编程后,我们将更加深入网络的学习——HTTP协议、TCP协议、UDP协议、IP协议........... IO多路复用 ✨在上一节基于 TCP 协议 编写应用层代码时,我们通过一个线程处理连接的申请,随后通过多线程或者线程…...
Bash Shell知识合集
1. chmod命令 创建一个bash shell脚本 hello.sh ~script $ touch hello.sh脚本创建完成后并不能直接执行,我们要用chmod命令授予它可执行的权限: ~script $ chmod 755 hello.sh授权后的脚本可以直接执行: ~script $ ./hello.sh2.指定运行…...
从0入门自主空中机器人-1【课程介绍】
关于本课程: 本次课程是一套面向对自主空中机器人感兴趣的学生、爱好者、相关从业人员的免费课程,包含了从硬件组装、机载电脑环境设置、代码部署、实机实验等全套详细流程,带你从0开始,组装属于自己的自主无人机,并让…...
Doris使用注意点
自己学习过程中整理,非官方 dws等最后用于查询的表可以考虑使用row存储加快查询,即用空间换时间duplicate key的选择要考虑最常查询使用适当使用bloomfilter 加速查询适当使用aggregate 模式降低max,avg,min之类的计算并加快查询…...

Mybatis插件better-mybatis-generator的下载与使用
1.下载 找到设置 插件 搜索better-mybatis-generator 下载并且重启IDEA 2.连接数据库 点击测试连接 连接成功如下图 3.使用插件 选择对应的表 右击选择 注意:mysql8.0驱动一定要勾上mysql_8 其他地方不要动 然后实体类 mapper xml就都生成好了 mapper里有默认增删…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...