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? = null
val buf = ByteArray(2048)
var len = 0
var 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 = 0
while (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.contentResolver
val 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? = null
var ous: BufferedOutputStream? = null
return try {
ins = FileInputStream(tempFile)
ous = BufferedOutputStream(FileOutputStream(destFile))
var nread = 0L
val buf = ByteArray(1024)
var n: Int
while (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
}
}