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

最新文章

  1. Linux下面安装MySQL
  2. 彻底理解ThreadLocal一
  3. 浅析SqlServer简单参数化模式下对sql语句自动参数化处理以及执行计划重用
  4. Java 6.15习题
  5. TCP报文段首部详解
  6. python pydoc
  7. homework-03 图形化化最大子序列和
  8. Codeforces 551E - GukiZ and GukiZiana(分块)
  9. 全面理解Java内存模型
  10. abp中文件下载,将内存数据导出到Excel并下载
  11. Android上下文菜单ContentView详解
  12. 跟我一起学opencv 第二课之图像的掩膜操作
  13. 【WEB】带边框的网页页面实现
  14. UI 滑动面板的移动控制
  15. centos7 下安装Docker CE
  16. value,innerHTML,innerText之间的区别
  17. [转载]WIN7已经记住访问另一台电脑的账号和密码 凭证
  18. js 简易模板引擎 , 持续更新。。。
  19. 超强IIS站点工具一键设置PHP,支持多个PHP同时运行
  20. 使用辗转相除法求两个数的最大公因数(python实现)

热门文章

  1. 蓝桥杯近三年初赛题之二(16年b组)
  2. log4j:WARN No appenders could be found for logger 解决方案
  3. 更改linux终端中用户名颜色
  4. python 文本处理操作
  5. 软件工程---UML理解
  6. Redis学习-常用命令
  7. EDK II之Device Path
  8. php 根据日期获取星座
  9. 2019 GOALS
  10. tomcat 修改内存配置