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; }