Ringtone的两种类型三种形式
文章目錄
-
- Ringtone 的兩中類型三種形式
-
- 兩種類型
- 三種形式
-
- 1 最常見的uri形式
- 2 Settings數據庫中的uri形式
- 3 Android 4.4以后增加了document uri
- 返回給RingtonePickerActivity的uri處理
-
- authority是settings
- authority是com.android.providers...
- RingtonePickerActivity的默認鈴聲
- RingtonePickerActivity的初始鈴聲列表
Ringtone 的兩中類型三種形式
兩種類型
指的是:external.db和internal.db中對應的MediaStore.Audio.Media.EXTERNAL_CONTENT_URI和MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
三種形式
1 最常見的uri形式
content://media/external/audio/media/id 和 content://media/internal/audio/media/id
id就是其在MediaProvider中對應的數據庫中的id
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI---content://media/external/audio/media/id
對應的數據庫:/data/data/com.android.providers.media/databases/external.db
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI---content://media/internal/audio/media/id
對應的數據庫:/data/data/com.android.providers.media/databases/internal.db
authority是media
2 Settings數據庫中的uri形式
content://setting/system/***
Settings.System.DEFAULT_NOTIFICATION_URI----content://setting/system/notification_sound
Settings.System.DEFAULT_RINGTONE_URI------content://settings/system/ringtone
Settings.System.DEFAULT_ALARM_ALERT_URI-----content://settings/system/alarm_alert
authority是settings
android M以前對應的數據庫data\data\com.android.providers.settings\databases\這個目錄下的Settings.db (如android L)
android M SettingsProvider數據庫的變化,變化如下,之前系統修改的數據最后是存儲在data/data/com.android.providers.settings/databases目錄下,現在該目錄下存放了一個backup數據庫,里面的數據不是當前系統設置的全部數據,只有一部分內容,另一個是journal數據庫,無數據。現在的數據庫真正的數據存儲目錄在data/system/users/userId(我們沒開啟多用戶,userid為0),數據存儲形式不是以db的形式,如下截圖,為數據三個表分別對應system,secure和global,查看相應數據可以導出對應的xml。
settings_system.xml中存儲的就有content://setting/system/..,存儲的value是第一種形式,所以其本質是把第一種數據uri形式存儲在settings_system.xml中。
settings_system.xml 所有的偏好設置對系統的所有用戶公開,第三方APP有讀沒有寫的權限;
settings_global.xml 包含各種各樣的用戶偏好系統設置;
settings_secure.xml 安全性的用戶偏好系統設置,第三方APP有讀沒有寫的權限
這里可以直接adb修改配置值,方便開發人員測試:
adb shell settings put system xxxx 1
adb shell settings get system xxx
adb shell settings put global xxx 0
3 Android 4.4以后增加了document uri
content://com.android.providers.media.documents/document/audio%3A82482
content://com.android.providers.downloads.documents/document/audio%3A82556
content://com.android.externalstorage.documents//document/audio%8N584645
authority是com.android.providers.*.documents
其DocumentsContract.getDocumentId(uri)進行String串的處理獲取id轉化為第一種形式,本質上是對第一種形式的包裝
if (isMediaDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String id = split[1];Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/media/"), Long.valueOf(id));return contentUri;...
不管那種類型或形式都是下面這種格式:
content://authority/path/id
返回給RingtonePickerActivity的uri處理
返回給RingtonePickerActivity的uri如果不是第三中形式需要對uri處理轉化為第一種形式
authority是settings
通過訪問settings_system.xml獲取對應的value,也就是存儲的第一種形式的content uri的String形式
String uriString = Settings.System.getString(context.getContentResolver(), Settings.System.RINGTONE.toString());
authority是com.android.providers…
返回給RingtonePickerActivity的uri如果不是第三中形式需要對uri處理轉化為第一種形式 authority是com.android.providers…
這種有三種,需要分別處理如下:
/*** Gets the content:// URI from the given corresponding path to a file** @param context* @param path* @return content Uri*/private static Uri getContentUriWithPath(ContentResolver resolver, String path) {Cursor cursor = null;String filePath = path;try {cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,new String[] { MediaStore.Audio.Media._ID },MediaStore.Audio.Media.DATA + "=? ",new String[] { filePath }, null);if (cursor != null && cursor.moveToFirst()) {int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));Uri baseUri = Uri.parse("content://media/external/audio/media");return Uri.withAppendedPath(baseUri, "" + id);} else {if (filePath != null) {ContentValues values = new ContentValues();values.put(MediaStore.Audio.Media.DATA, filePath);return resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);} else {return null;}}}finally {if (cursor != null)cursor.close();}}public static String getDataColumn(ContentResolver resolver, Uri uri, String selection,String[] selectionArgs) {Cursor cursor = null;final String column = "_data";final String[] projection = { column };try {cursor = resolver.query(uri, projection, selection, selectionArgs,null);if (cursor != null && cursor.moveToFirst()) {final int index = cursor.getColumnIndexOrThrow(column);//Log.d(TAG, "cursor.getString(index) = " + cursor.getString(index));return cursor.getString(index);}} finally {if (cursor != null)cursor.close();}return null;}private Uri getContentUri(ContentResolver resolver, Uri uri){if (DocumentsContract.isDocumentUri(this, uri)) {if (isMediaDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String id = split[1];Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/media/"), Long.valueOf(id));return contentUri;}else if (isExternalStorageDocument(uri)){//Log.d(TAG, "****ExternalStorageDocument****");final String docId = DocumentsContract.getDocumentId(uri);//Log.d(TAG, "getContentUri: docId = " + docId);final String[] split = docId.split(":");final String type = split[0];String path = null;if ("primary".equalsIgnoreCase(type)) {path = Environment.getExternalStorageDirectory() + "/" + split[1];//Log.d(TAG, "getContentUri: primary path = " + path);}else{path = "/storage/" + split[0] + "/" + split[1];//Log.d(TAG, "getContentUri: path = " + path);}return getContentUriWithPath(resolver,path);}else if (isDownloadsDocument(uri)){Log.d(TAG, "****DownloadsDocument****");final String id = DocumentsContract.getDocumentId(uri);//Log.d(TAG, "getContentUri ,getDocumentId: Id = " + id);final Uri downloadsUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));//Log.d(TAG, "downloadsUri:" + downloadsUri.toString());String path = getDataColumn(resolver, downloadsUri, null, null);return getContentUriWithPath(resolver,path);}}return uri;}/*** @param uri The Uri to check.* @return Whether the Uri authority is ExternalStorageProvider.*/public static boolean isExternalStorageDocument(Uri uri) {return "com.android.externalstorage.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is DownloadsProvider.*/public static boolean isDownloadsDocument(Uri uri) {return "com.android.providers.downloads.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is MediaProvider.*/public static boolean isMediaDocument(Uri uri) {return "com.android.providers.media.documents".equals(uri.getAuthority());}
RingtonePickerActivity的默認鈴聲
/platform/build / target/product/full_base.mk
PRODUCT_PROPERTY_OVERRIDES := \ro.config.ringtone=Ring_Synth_04.ogg \ro.config.notification_sound=pixiedust.ogg......
RingtonePickerActivity的初始鈴聲列表
/platform/build / target/product/full_base.mk中會copy一部分internal ringtone
$(call inherit-product-if-exists, frameworks/base/data/sounds/AllAudio.mk)
AllAudio.mk會把audio資源copy到系統Ringtones、Notifications、Alarms目錄下,也可在vendor下定制自己的鈴聲列表和默認鈴聲
在MediaScanner掃描文件時,判斷到如果文件的路徑含有Ringtones,則認為它是鈴聲,并將其在database的is_ringtone這個屬性的欄位置為1,RingtonePickerActivity的初始鈴聲列表就是這些is_ringtone屬性為1的資源
鈴聲列表查詢鈴聲時,使用的過濾條件就是is_ringtone 為1的鈴聲,所以會將所有is_ringtone 為1的鈴聲過濾出來,Hangouts Call /Hangouts Message在應用的Ringtones目錄中且is_ringtone 屬性為1,也會在“鈴聲列表“中被過濾出來因此Hangouts Call.ogg /Hangouts Message.ogg會出現在ringtones中,出現在Notification中是同樣的原因。
可以去除Hangouts Call /Hangouts Message顯示在初始鈴聲列表:
frameworks / base/media/java/android/media/MediaScanner.java
//ADD BEGIN
private static final String SYS_RINGTONES_DIR = "system/media/audio/ringtones/";
private static final String SYS_NOTIFICATIONS_DIR = "system/media/audio/notifications/";
//ADD END
......
public Uri doScanFile(String path, String mimeType, long lastModified,long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) {Uri result = null;try {...if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {if (noMedia) {result = endFile(entry, false, false, false, false, false);} else {String lowpath = path.toLowerCase(Locale.ROOT);//MOD BEGINboolean ringtones = (lowpath.indexOf(SYS_RINGTONES_DIR) > 0);boolean notifications = (lowpath.indexOf(SYS_NOTIFICATIONS_DIR) > 0);//MOD ENDboolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0);boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0);boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||(!ringtones && !notifications && !alarms && !podcasts);......
總結
以上是生活随笔為你收集整理的Ringtone的两种类型三种形式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Clumsy的使用方法
- 下一篇: 快充线突然不能快充是怎么回事