1. Android7.0,将存储卡中MP3设置为铃声,删除该MP3后,settings中的铃声没有变化,来电铃声也没有变化。

原因:android7.0的新特性

google 默认如此设计,在选择铃声的过程中,会将删除的铃声进行缓存,在删除铃声后,播放为缓存文件 
          
              1. google 目前将铃声分为actual default ringtone和cache ringtone,前者以ringtone为key将文件uri存储在xml文件里,后者是以stream file的形式存储在 ringtone_cache 的resource中。 
  
              2. 在铃声初始化的时候,铃声是初始化在actual default ringtone里,这个是可变的。不再是N版本之前把default铃声写死。 
  
              3. 设置铃声时,会同时写actual default ringtone 和 ringtone_cache。 
  
              4. 响铃时,mediaplayer.java会优先播放ringtone_cache里的stream resource文件,所以就算原音乐档被删除,依旧会响该备份铃声。 
  
              5. 闹铃、通知音等,原理同上。

解决办法:如果需要在删除MP3后将来电铃声恢复为默认铃声,可以这么做:

1.从settings中进入铃声选择界面,会调用getActualDefaultRingtoneUri,获取当前的铃声uri。那么我们需要在每次获取uri的时候,进行判断,该uri的音乐是否还存在。判断方法:

    public static boolean isRingtoneExist(Context context, Uri uri) {
if (uri == null) {
Log.e(TAG, "Check ringtone exist with null uri!");
return false;
}
boolean exist = false;
try {
AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(uri, "r");
if (fd == null) {
exist = false;
} else {
fd.close();
exist = true;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
exist = false;
} catch (IOException e) {
e.printStackTrace();
exist = true;
}
Log.d(TAG, uri + " is exist " + exist);
return exist;
}
    public static boolean isRingtoneExistByQueryDb(Context context, Uri uri){
if (uri == null) {
Log.e(TAG, "isRingtoneExistByQueryDb --> Check ringtone exist with null uri!");
return false;
}
boolean exist = false;
try {
Cursor cursor = context.getContentResolver().query(uri
,new String[]{MediaStore.Files.FileColumns.DATA},null,null,null);
if (cursor != null && cursor.moveToFirst()) {
int path_index = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA);
String path = cursor.getString(path_index);
Log.d(TAG,"isRingtoneExistByQueryDb path = " + path);
if(path != null && !"".equals(path)){
exist = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d(TAG,"isRingtoneExistByQueryDb " + uri + " is exist = " + exist);
return exist;
}

代码路径:/frameworks/base / media/java/android/media/RingtoneManager.java

如果该uri音乐已经不存在,那么将默认uri设置为铃声。由于7.0的默认铃声不再写死,是可变的(亲测是这样,但没具体看是怎么做的),因此我们需要自己将第一次开机时读到的默认uri存起来。

        private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
ContentResolver cr = mContext.getContentResolver();
String existingSettingValue = Settings.System.getString(cr, settingName); // if (TextUtils.isEmpty(existingSettingValue)) {
// final Uri settingUri = Settings.System.getUriFor(settingName);
// final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
// RingtoneManager.setActualDefaultRingtoneUri(mContext,
// RingtoneManager.getDefaultType(settingUri), ringtoneUri);
// /// M: Adds log to debug setting ringtones.
// if (DEBUG) {
// Log.v(TAG, "setSettingIfNotSet: name="
// + settingName + ",value=" + rowId);
// }
// }
if (TextUtils.isEmpty(existingSettingValue)) {
// Set the setting to the given URI
Settings.System.putString(mContext.getContentResolver(), settingName,
ContentUris.withAppendedId(uri, rowId).toString());
/// M: Adds log to debug setting ringtones.
Log.v(TAG, "setSettingIfNotSet: name=" + settingName + ",value=" + rowId);
} else {
/// M: Adds log to debug setting ringtones.
Log.e(TAG, "setSettingIfNotSet: name=" + settingName + " with value=" + existingSettingValue);
}
}

代码路径:/frameworks/base / media/java/android/media/MediaScanner.java

该settingsProvider字段是我们自己定义的,在RingtoneManager.java中。

    /// M: Add for store and get default ringtone @{
/**
* M: The key used to store the default ringtone of voice call.
* @hide
* @internal
*/ public static final String KEY_DEFAULT_RINGTONE = "mtk_audioprofile_default_ringtone";
/**
* M: The key used to store the default notification sound.
* @hide
* @internal
*/
public static final String KEY_DEFAULT_NOTIFICATION = "mtk_audioprofile_default_notification"; /**
* M:The key used to store the default alarm sound.
* @hide
*/
public static final String KEY_DEFAULT_ALARM = "mtk_audioprofile_default_alarm";
/// @}
    public static Uri getDefaultRingtoneUri(Context context, int type) {
Uri defaultUri = null;
String uriString = null;
ContentResolver resolver = context.getContentResolver();
switch (type) {
case TYPE_RINGTONE:
uriString = Settings.System.getString(resolver, KEY_DEFAULT_RINGTONE);
break; case TYPE_NOTIFICATION:
uriString = Settings.System.getString(resolver, KEY_DEFAULT_NOTIFICATION);
break; case TYPE_ALARM:
uriString = Settings.System.getString(resolver, KEY_DEFAULT_ALARM);
break; default:
Log.e(TAG, "getDefaultRingtoneUri with unsupport type!");
return null;
}
defaultUri = (uriString == null ? null : Uri.parse(uriString));
Log.d(TAG, "getDefaultRingtoneUri: type = " + type + ", default uri = " + defaultUri);
return defaultUri;
}

那么最后,就可以在getActualDefaultRingtoneUri()中进行判断啦!

   public static Uri getActualDefaultRingtoneUri(Context context, int type) {
String setting = getSettingForType(type);
if (setting == null) return null;
final String uriString = Settings.System.getStringForUser(context.getContentResolver(),
setting, context.getUserId());
try{ boolean isExist = isRingtoneExist(context, Uri.parse(uriString)) || isRingtoneExistByQueryDb(context, Uri.parse(uriString));
Log.d(TAG,"getActualDefaultRingtoneUri isExist = "+isExist);
if (uriString != null && !isExist){
Log.i(TAG, "Get actual default setdefaultURi= " + uriString);
Uri defaultUri = getDefaultRingtoneUri(context,type);
setActualDefaultRingtoneUri(context,type,defaultUri);
return defaultUri;
}
Log.d(TAG,"getActualDefaultRingtoneUri 2222 uriString = "+uriString);
}catch(Exception ex){
Log.d(TAG, ex.getMessage());
}
Log.i(TAG, "Get actual default ringtone uri= " + uriString);
return uriString != null ? Uri.parse(uriString) : null;
}

============================================================

另外多说一嘴,怎么样可以直接在settings中的铃声选择里,加上存储卡音乐的选择呢(回想起在zs工作的时候,直接把铃声选择做到了settings里,可是埋了很大的坑)

        /// M: Get whether to show the 'More Ringtones' item
mHasMoreRingtonesItem = intent.getBooleanExtra(
RingtoneManager.EXTRA_RINGTONE_SHOW_MORE_RINGTONES, true);

原生的参数是false,改成true即可,直接的接口可以用,敲方便!

代码路径:packages/providers/MediaProvider / src/com/android/providers/media/RingtonePickerActivity.java

将SD卡的音频设置为手机铃声后删除,手机铃声没有恢复到默认的问题的更多相关文章

  1. android 读写sd卡的权限设置

    原文:android 读写sd卡的权限设置 在Android中,要模拟SD卡,要首先使用adb的mksdcard命令来建立SD卡的镜像,如何建立,大家上网查一下吧,应该很容易找到,这里不说这个问题. ...

  2. am335x sd卡启动系统参数设置

    首先直接记录结果 在u-boot 中修改参数 #define AUTO_UPDATESYS */ 直接把这个参数注释掉. 这个参数是原来用来升级nor flash 启动系统设置的一个参数,也就是说, ...

  3. sd卡无法启动及zc706更改主频后可以进入uboot无法启动kernel的坑

    好长的标题 +_+ 1.sd卡无法启动 起因:kernel底下通过dd测试速度,擦写了sd卡,再启动时发现无法启动 于是重新格式化,再将BOOT.bin 相关dtb u-rootfs zImage和u ...

  4. 第36章 SDIO—SD卡读写测试

    第36章     SDIO—SD卡读写测试 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/f ...

  5. 第36章 SDIO—SD卡读写测试—零死角玩转STM32-F429系列

    第36章     SDIO—SD卡读写测试 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/f ...

  6. Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建

    /*********************************************************************************** * * linux 3.5,U ...

  7. linux(ubuntu)下分区和格式化sd卡

    我的手机sd卡需要分成两个分区,在windowxp下面死活搞不成.主要的问题是,window只认识sd卡的第一个分区.有人用修改驱动程序,让windows把sd卡认成日立的microdisk,分区和格 ...

  8. SPI模式下MCU对SD卡的控制及操作命令

    一.前言 SD 卡有两个可选的通讯协议:SD 模式和 SPI模式 SD 模式是SD 卡标准的读写方式,但是在选用SD 模式时,往往需要选择带有SD 卡控制器接口的 MCU,或者必须加入额外的SD卡控制 ...

  9. 图文:TF卡和SD卡的区别及什么是TF卡?什么是SD卡

    小型存储设备凭借低廉的价格.多样化的品种.实用等特性大量充斥在大家身边,比如智能手机手机上.数码照相机上.游戏机上(一般是掌机)等都小型电子设备都频繁的使用到这种统称为SD的产品,比如TF卡和SD卡( ...

随机推荐

  1. centos共享目录

    使用VirtualBOX自带的共享文件夹功能 运行环境:  - 宿主机:windows 10  - 虚拟机:CentOS 7(我当初是按照Ubuntu环境下的教程弄的,所以其他Linux版本也可以参考 ...

  2. html5 javascript 事件练习3键盘控制练习

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  3. vue的一些感想

    如今vue2.0是主流,但是它的路由确实直接从1.0过来的,其中包括组件包括全局组件和局部组件,写好组件之后,我们就需要 使用路由,将组件关联起来,关联起来之后,然后我们才可以将组件的内容通过hash ...

  4. codeforces 985E Pencils and Boxes

    题意: 把一个数组分成若干组,保证每组的size >= k并且一组中任意两个数字的差的绝对值 <= d,问存不存在这样的分法. 思路: 线性dp. 用dp[i]表示前i个数是否有分法. 设 ...

  5. c#阿里云服务器发送邮件

    public static void SendMailUse() { string host = "smtp.lotusest.com";// 邮件服务器smtp.163.com表 ...

  6. C++的重载操作符(operator)介绍(转)

    本文主要介绍C++中的重载操作符(operator)的相关知识. 1. 概述 1.1 what operator 是C++的一个关键字,它和运算符(如=)一起使用,表示一个运算符重载函数,在理解时可将 ...

  7. IDEA设置(含永久破解IDEA)

    永久破解IDEA(很多license服务器都是非永久性的,太麻烦了) https://www.cnblogs.com/iathanasy/p/9469280.html,亲测. 在我们为 IDEA 等编 ...

  8. 【Alpha】Scrum Meeting 3

    目录 简介: 工作内容: 工作修改: 燃尽图: 难点: 后期任务: 签入记录 团队讨论照片: 简介: 地点:J1-316 时间:4月3日星期四,晚上七点 会议目的:讨论大家此次的分工 工作内容: 陈治 ...

  9. shadows

    http://blog.51cto.com/11975865/2308030 https://www.jianshu.com/p/247cdbabf389 https://baijiahao.baid ...

  10. Win32汇编学习(9):窗口控件

    这次我们将探讨控件,这些控件是我们程序主要的输入输出设备. 理论: WINDOWS 提供了几个预定义的窗口类以方便我们的使用.大多数时间内,我们把它们用在对话框中,所以我们一般就它们叫做子窗口控件.子 ...