MediaProvider.java revision 3e6a42887a17a93b8906ff05693587e25158f2b1
1702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/* 2702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 3702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 4702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * you may not use this file except in compliance with the License. 6702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * You may obtain a copy of the License at 7702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 8702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 10702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * See the License for the specific language governing permissions and 14702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * limitations under the License. 15702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 16702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 17702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpackage com.android.providers.media; 18702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 19007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.ACCESS_CACHE_FILESYSTEM; 20007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.READ_EXTERNAL_STORAGE; 21007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; 22f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_READ_ONLY; 23007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; 24007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 25702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager; 26bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.BroadcastReceiver; 27bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ComponentName; 28bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProvider; 29bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderOperation; 30bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderResult; 31bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentResolver; 32bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentUris; 33bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentValues; 34bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Context; 35bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Intent; 36bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.IntentFilter; 37bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.OperationApplicationException; 38bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ServiceConnection; 39ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.content.SharedPreferences; 40bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher; 413e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissenimport android.content.pm.PackageManager; 4290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissenimport android.content.pm.PackageManager.NameNotFoundException; 4370676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources; 44702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor; 45ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils; 460027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor; 47702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase; 48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper; 49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder; 50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap; 51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory; 52b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile; 53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner; 5438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection; 5538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection.MediaScannerConnectionClient; 56b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile; 5790345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants; 589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwoodimport android.mtp.MtpStorage; 59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri; 60702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder; 6138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.os.Bundle; 62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment; 63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils; 64702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler; 65ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread; 66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message; 67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor; 68702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process; 69d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException; 7010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.os.SystemClock; 71c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageManager; 721f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwoodimport android.os.storage.StorageVolume; 73ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager; 74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns; 75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore; 76702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio; 774eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissenimport android.provider.MediaStore.Audio.Playlists; 78a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Linimport android.provider.MediaStore.Files; 7970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns; 80702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images; 8170676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns; 82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns; 83702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video; 84702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils; 8510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils; 86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log; 87702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 88702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File; 8910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor; 90702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream; 91702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException; 92702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException; 93702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream; 9410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter; 95cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList; 9610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection; 97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap; 98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet; 99702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator; 100f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List; 10138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Locale; 102b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue; 1038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack; 104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 105166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.ErrnoException; 10620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissenimport libcore.io.IoUtils; 107166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.Libcore; 10870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissenimport libcore.io.OsConstants; 10970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissenimport libcore.io.StructStat; 110166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen 111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/** 112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details. 113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the 114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index). The content visible at content://media/external/... 115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card. 116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider { 118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final Uri MEDIA_URI = Uri.parse("content://media"); 119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart"); 120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int ALBUM_THUMB = 1; 121b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int IMAGE_THUMB = 2; 122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>(); 124d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>(); 125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 126007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** Resolved canonical path to external storage. */ 127007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private static final String sExternalPath; 128007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** Resolved canonical path to cache storage. */ 129007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private static final String sCachePath; 130007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 131007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey static { 132007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey try { 133007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey sExternalPath = Environment.getExternalStorageDirectory().getCanonicalPath(); 134007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey sCachePath = Environment.getDownloadCacheDirectory().getCanonicalPath(); 135007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } catch (IOException e) { 136007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new RuntimeException("Unable to resolve canonical paths", e); 137007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 138007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 139007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 1407f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // In memory cache of path<->id mappings, to speed up inserts during media scan 1417f36494e085c26c69cd5925e54028822025eff29Marco Nelissen HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>(); 1427f36494e085c26c69cd5925e54028822025eff29Marco Nelissen 1438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // A HashSet of paths that are pending creation of album art thumbnails. 1448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private HashSet mPendingThumbs = new HashSet(); 1458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 1468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // A Stack of outstanding thumbnail requests. 1478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private Stack mThumbRequestStack = new Stack(); 1488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 14920434e032e498b716f87cce2f23dd646819218bfRay Chen // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest. 15020434e032e498b716f87cce2f23dd646819218bfRay Chen private MediaThumbRequest mCurrentThumbRequest = null; 151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private PriorityQueue<MediaThumbRequest> mMediaThumbQueue = 152b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL, 153b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaThumbRequest.getComparator()); 154b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 155f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood private boolean mCaseInsensitivePaths; 1569be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood private static String[] mExternalStoragePaths; 15717ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood 158a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // For compatibility with the approximately 0 apps that used mediaprovider search in 159a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // releases 1.0, 1.1 or 1.5 160a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsLegacy = new String[] { 161a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist + 164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album + 165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE " + R.drawable.ic_search_category_music_song + " END END" + 166a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1, 167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2, 168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, 169a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY, 170a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE when grouporder=1 THEN data1 ELSE artist END AS data1", 171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE when grouporder=1 THEN data2 ELSE " + 172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2", 173a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "match as ar", 174a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen SearchManager.SUGGEST_COLUMN_INTENT_DATA, 175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "grouporder", 176ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that 177ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen // column is not available here, and the list is already sorted. 178a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 179a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsFancy = new String[] { 180a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 181a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 182a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Artists.ARTIST, 183a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Albums.ALBUM, 184a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.TITLE, 185a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "data1", 186a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "data2", 187a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 18863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath // If this array gets changed, please update the constant below to point to the correct item. 189a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsBasic = new String[] { 190a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 191a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 192a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist + 193a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album + 194a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE " + R.drawable.ic_search_category_music_song + " END END" + 195a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1, 196a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, 197a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY, 19863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath "(CASE WHEN grouporder=1 THEN '%1'" + // %1 gets replaced with localized string. 19963f748ff8b258d9110038778a006b3000164fbeeSatish Sampath " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" + 200e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" + 20163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2, 202a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen SearchManager.SUGGEST_COLUMN_INTENT_DATA 203a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 20463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath // Position of the TEXT_2 item in the above array. 20563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath private final int SEARCH_COLUMN_BASIC_TEXT2 = 5; 206a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 207a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen private static final String[] sMediaTableColumns = new String[] { 20816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood FileColumns._ID, 209afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood FileColumns.MEDIA_TYPE, 2101717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood }; 2111717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 2127f36494e085c26c69cd5925e54028822025eff29Marco Nelissen private static final String[] sIdOnlyColumn = new String[] { 2137f36494e085c26c69cd5925e54028822025eff29Marco Nelissen FileColumns._ID 2147f36494e085c26c69cd5925e54028822025eff29Marco Nelissen }; 2157f36494e085c26c69cd5925e54028822025eff29Marco Nelissen 216166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen private static final String[] sDataOnlyColumn = new String[] { 217166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen FileColumns.DATA 218166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen }; 219166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen 220a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen private static final String[] sMediaTypeDataId = new String[] { 221a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen FileColumns.MEDIA_TYPE, 222a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen FileColumns.DATA, 223a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen FileColumns._ID 2244eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen }; 2254eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen 2264eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen private static final String[] sPlaylistIdPlayOrder = new String[] { 2274eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Playlists.Members.PLAYLIST_ID, 2284eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Playlists.Members.PLAY_ORDER 2294eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen }; 230a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen 231a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart"); 232a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen 233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() { 234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onReceive(Context context, Intent intent) { 236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) { 2371f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood StorageVolume storage = (StorageVolume)intent.getParcelableExtra( 2381f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood StorageVolume.EXTRA_STORAGE_VOLUME); 2391f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // If primary external storage is ejected, then remove the external volume 2401f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // notify all cursors backed by data on that volume. 2411f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood if (storage.getPath().equals(mExternalStoragePaths[0])) { 2421f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood detachVolume(Uri.parse("content://media/external")); 2431f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood sFolderArtMap.clear(); 2441f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood MiniThumbFile.reset(); 2451f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } else { 2461f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // If secondary external storage is ejected, then we delete all database 2471f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // entries for that storage from the files table. 2481f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood synchronized (mDatabases) { 2491f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood DatabaseHelper database = mDatabases.get(EXTERNAL_VOLUME); 2501f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Uri uri = Uri.parse("file://" + storage.getPath()); 2511f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood if (database != null) { 2521f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood try { 2531f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // Send media scanner started and stopped broadcasts for apps that rely 2541f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // on these Intents for coarse grained media database notifications. 2551f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood context.sendBroadcast( 2561f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri)); 2571f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood 2581f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // don't send objectRemoved events - MTP be sending StorageRemoved anyway 2591f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood mDisableMtpObjectCallbacks = true; 2601f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Log.d(TAG, "deleting all entries for storage " + storage); 2611f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood SQLiteDatabase db = database.getWritableDatabase(); 2624b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // First clear the file path to disable the _DELETE_FILE database hook. 2634b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // We do this to avoid deleting files if the volume is remounted while 2644b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // we are still processing the unmount event. 2654b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood ContentValues values = new ContentValues(); 2664b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood values.put(Files.FileColumns.DATA, ""); 2674b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood String where = FileColumns.STORAGE_ID + "=?"; 2684b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) }; 2699b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumUpdates++; 2704b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood db.update("files", values, where, whereArgs); 2714b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // now delete the records 2729b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumDeletes++; 2739b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen int numpurged = db.delete("files", where, whereArgs); 2749b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen logToDb(db, "removed " + numpurged + 2759b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen " rows for ejected filesystem " + storage.getPath()); 2764b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // notify on media Uris as well as the files Uri 2774b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood context.getContentResolver().notifyChange( 2784b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood Audio.Media.getContentUri(EXTERNAL_VOLUME), null); 2794b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood context.getContentResolver().notifyChange( 2804b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood Images.Media.getContentUri(EXTERNAL_VOLUME), null); 2814b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood context.getContentResolver().notifyChange( 2824b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood Video.Media.getContentUri(EXTERNAL_VOLUME), null); 2831f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood context.getContentResolver().notifyChange( 2841f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Files.getContentUri(EXTERNAL_VOLUME), null); 2851f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } catch (Exception e) { 2861f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Log.e(TAG, "exception deleting storage entries", e); 2871f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } finally { 2881f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood context.sendBroadcast( 2891f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri)); 2901f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood mDisableMtpObjectCallbacks = false; 2911f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 2921f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 2931f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 2941f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 299d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // set to disable sending events when the operation originates from MTP 300d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private boolean mDisableMtpObjectCallbacks; 301d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 302d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private final SQLiteDatabase.CustomFunction mObjectRemovedCallback = 303d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood new SQLiteDatabase.CustomFunction() { 304d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void callback(String[] args) { 3057f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // We could remove only the deleted entry from the cache, but that 3067f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // requires the path, which we don't have here, so instead we just 3077f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // clear the entire cache. 3087f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // TODO: include the path in the callback and only remove the affected 3097f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // entry from the cache 3107f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.clear(); 311d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // do nothing if the operation originated from MTP 312d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (mDisableMtpObjectCallbacks) return; 313d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 314d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood Log.d(TAG, "object removed " + args[0]); 315d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood IMtpService mtpService = mMtpService; 316d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (mtpService != null) { 317d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood try { 318d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood sendObjectRemoved(Integer.parseInt(args[0])); 319d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } catch (NumberFormatException e) { 320d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e); 321d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 322d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 323d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 324d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood }; 325d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Wrapper class for a specific database (associated with one particular 328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * external card, or with internal storage). Can open the actual database 329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * on demand, create and upgrade the schema, etc. 330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 331fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn static final class DatabaseHelper extends SQLiteOpenHelper { 332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final Context mContext; 3335524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood final String mName; 334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final boolean mInternal; // True if this is the internal database 335fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn final boolean mEarlyUpgrade; 336fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn final SQLiteDatabase.CustomFunction mObjectRemovedCallback; 3375524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood boolean mUpgradeAttempted; // Used for upgrade error handling 33810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumQueries; 33910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumUpdates; 34010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumInserts; 34110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumDeletes; 34210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long mScanStartTime; 34310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long mScanStopTime; 344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // In memory caches of artist and album data. 346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> mArtistCache = new HashMap<String, Long>(); 347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> mAlbumCache = new HashMap<String, Long>(); 348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 349fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn public DatabaseHelper(Context context, String name, boolean internal, 350fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn boolean earlyUpgrade, 351fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn SQLiteDatabase.CustomFunction objectRemovedCallback) { 35290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen super(context, name, null, getDatabaseVersion(context)); 353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext = context; 3545524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mName = name; 355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mInternal = internal; 356fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn mEarlyUpgrade = earlyUpgrade; 357fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn mObjectRemovedCallback = objectRemovedCallback; 3580e2a2386b39972286df21f4db5a9dd1df548c34dJeff Brown setWriteAheadLoggingEnabled(true); 359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Creates database the first time we try to open it. 363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onCreate(final SQLiteDatabase db) { 36690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen updateDatabase(mContext, db, mInternal, 0, getDatabaseVersion(mContext)); 367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Updates the database format when a new content provider is used 371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * with an older database format. 372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) { 3755524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mUpgradeAttempted = true; 37690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen updateDatabase(mContext, db, mInternal, oldV, newV); 377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 379db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin @Override 380db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin public synchronized SQLiteDatabase getWritableDatabase() { 3815524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood SQLiteDatabase result = null; 3825524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mUpgradeAttempted = false; 3835524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood try { 3845524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood result = super.getWritableDatabase(); 3855524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } catch (Exception e) { 3865524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood if (!mUpgradeAttempted) { 3875524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood Log.e(TAG, "failed to open database " + mName, e); 3885524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood return null; 3895524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 3905524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 3915524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood 3925524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // If we failed to open the database during an upgrade, delete the file and try again. 3935524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // This will result in the creation of a fresh database, which will be repopulated 3945524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // when the media scanner runs. 3955524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood if (result == null && mUpgradeAttempted) { 396450d884f1bd5de323a645ce1acfae40fb91b8cb0jangwon.lee mContext.deleteDatabase(mName); 3975524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood result = super.getWritableDatabase(); 3985524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 3995524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood return result; 4005524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 4015524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood 402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 403993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * For devices that have removable storage, we support keeping multiple databases 404993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * to allow users to switch between a number of cards. 405993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * On such devices, touch this particular database and garbage collect old databases. 406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * An LRU cache system is used to clean up databases for old external 407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * storage volumes. 408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onOpen(SQLiteDatabase db) { 41136d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky 412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mInternal) return; // The internal database is kept separately. 413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 414fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (mEarlyUpgrade) return; // Doing early upgrade. 415fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn 416fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (mObjectRemovedCallback != null) { 417fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback); 418fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 419d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 420993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // the code below is only needed on devices with removable storage 421993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (!Environment.isExternalStorageRemovable()) return; 422993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood 423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // touch the database file to show it is most recently used 424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(db.getPath()); 425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long now = System.currentTimeMillis(); 426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file.setLastModified(now); 427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used databases if we are over the limit 429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] databases = mContext.databaseList(); 430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count = databases.length; 431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int limit = MAX_EXTERNAL_DATABASES; 432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete external databases that have not been used in the past two months 434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long twoMonthsAgo = now - OBSOLETE_DATABASE_DB; 435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < databases.length; i++) { 436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File other = mContext.getDatabasePath(databases[i]); 437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) { 438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[i] = null; 439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (file.equals(other)) { 441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // reduce limit to account for the existence of the database we 442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // are about to open, which we removed from the list. 443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project limit--; 444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long time = other.lastModified(); 447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (time < twoMonthsAgo) { 448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]); 449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext.deleteDatabase(databases[i]); 450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[i] = null; 451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used databases until 457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we are no longer over the limit 458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (count > limit) { 459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int lruIndex = -1; 460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long lruTime = 0; 461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < databases.length; i++) { 463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (databases[i] != null) { 464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long time = mContext.getDatabasePath(databases[i]).lastModified(); 465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lruTime == 0 || time < lruTime) { 466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project lruIndex = i; 467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project lruTime = time; 468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used database 473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lruIndex != -1) { 474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]); 475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext.deleteDatabase(databases[lruIndex]); 476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[lruIndex] = null; 477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 48334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // synchronize on mMtpServiceConnection when accessing mMtpService 484d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private IMtpService mMtpService; 485d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 486d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private final ServiceConnection mMtpServiceConnection = new ServiceConnection() { 487d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void onServiceConnected(ComponentName className, android.os.IBinder service) { 48834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (this) { 48934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = IMtpService.Stub.asInterface(service); 49034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 491d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 492d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 493d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void onServiceDisconnected(ComponentName className) { 49434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (this) { 49534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 49634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 497d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 498d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood }; 499d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 500ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood private static final String[] sDefaultFolderNames = { 501ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_MUSIC, 502ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_PODCASTS, 503ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_RINGTONES, 504ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_ALARMS, 505ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_NOTIFICATIONS, 506ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_PICTURES, 507ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_MOVIES, 508ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_DOWNLOADS, 509ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_DCIM, 510ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood }; 511ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 512ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // creates default folders (Music, Downloads, etc) 51310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) { 514ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // Use a SharedPreference to ensure we only do this once. 515ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // We don't want to annoy the user by recreating the directories 516ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // after she has deleted them. 517ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); 518ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood if (prefs.getInt("created_default_folders", 0) == 0) { 519ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood for (String folderName : sDefaultFolderNames) { 520ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood File file = Environment.getExternalStoragePublicDirectory(folderName); 521ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood if (!file.exists()) { 522ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood file.mkdirs(); 52310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen insertDirectory(helper, db, file.getAbsolutePath()); 524ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 525ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 526ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 527ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood SharedPreferences.Editor e = prefs.edit(); 528ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.clear(); 529ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.putInt("created_default_folders", 1); 530ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.commit(); 531ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 532ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 533ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 5349311c7ff9d35ca3acc908da3da7a79fbf7a8da6bMarco Nelissen public static int getDatabaseVersion(Context context) { 53590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen try { 53690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen return context.getPackageManager().getPackageInfo( 53790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen context.getPackageName(), 0).versionCode; 53890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } catch (NameNotFoundException e) { 53990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen throw new RuntimeException("couldn't get version code for " + context); 54090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } 54190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } 54290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen 543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public boolean onCreate() { 545d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood final Context context = getContext(); 546d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 547acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " + 548acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums._ID); 549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album"); 550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key"); 551acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " + 552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.FIRST_YEAR); 553acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " + 554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.LAST_YEAR); 555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist"); 556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist"); 557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key"); 558acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " + 559acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums.NUMBER_OF_SONGS); 560acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " + 561acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums.ALBUM_ART); 562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 56363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] = 56463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll( 565d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood "%1", context.getString(R.string.artist_label)); 566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mDatabases = new HashMap<String, DatabaseHelper>(); 567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project attachVolume(INTERNAL_VOLUME); 568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT); 570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project iFilter.addDataScheme("file"); 571d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood context.registerReceiver(mUnmountReceiver, iFilter); 572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 573c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood StorageManager storageManager = 574c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood (StorageManager)context.getSystemService(Context.STORAGE_SERVICE); 575c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood mExternalStoragePaths = storageManager.getVolumePaths(); 5769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood 577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // open external database if external storage is mounted 578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String state = Environment.getExternalStorageState(); 579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (Environment.MEDIA_MOUNTED.equals(state) || 580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { 581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project attachVolume(EXTERNAL_VOLUME); 582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 584ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND); 585ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang ht.start(); 586ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang mThumbHandler = new Handler(ht.getLooper()) { 587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void handleMessage(Message msg) { 589b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (msg.what == IMAGE_THUMB) { 590b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mMediaThumbQueue) { 59120434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest = mMediaThumbQueue.poll(); 592b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 59320434e032e498b716f87cce2f23dd646819218bfRay Chen if (mCurrentThumbRequest == null) { 594b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Log.w(TAG, "Have message but no request?"); 595b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } else { 596b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen try { 59720434e032e498b716f87cce2f23dd646819218bfRay Chen File origFile = new File(mCurrentThumbRequest.mPath); 5984d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen if (origFile.exists() && origFile.length() > 0) { 59920434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.execute(); 6004d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } else { 6014d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen // original file hasn't been stored yet 6024d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen synchronized (mMediaThumbQueue) { 60320434e032e498b716f87cce2f23dd646819218bfRay Chen Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath); 6044d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } 6054d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } 606b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } catch (IOException ex) { 6071d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen Log.w(TAG, ex); 6081d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen } catch (UnsupportedOperationException ex) { 6091d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen // This could happen if we unplug the sd card during insert/update/delete 6101d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen // See getDatabaseForUri. 6111d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen Log.w(TAG, ex); 61222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren } catch (OutOfMemoryError err) { 61322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren /* 61422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * Note: Catching Errors is in most cases considered 61522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * bad practice. However, in this case it is 61622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * motivated by the fact that corrupt or very large 61722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * images may cause a huge allocation to be 61822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * requested and denied. The bitmap handling API in 61922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * Android offers no other way to guard against 62022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * these problems than by catching OutOfMemoryError. 62122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren */ 62222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren Log.w(TAG, err); 623b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } finally { 62420434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mCurrentThumbRequest) { 62520434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE; 62620434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.notifyAll(); 627b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 628b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 629b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 630b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } else if (msg.what == ALBUM_THUMB) { 631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ThumbData d; 632b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mThumbRequestStack) { 633b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen d = (ThumbData)mThumbRequestStack.pop(); 634b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 6358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 636d63eb65db514ad7064951f221f0278a3f2293411Jeff Sharkey IoUtils.closeQuietly(makeThumbInternal(d)); 637b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mPendingThumbs) { 638b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen mPendingThumbs.remove(d.path); 639b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 6408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return true; 645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 647395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_FILES = "files"; 648395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_ALBUM_ART = "album_art"; 649395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_THUMBNAILS = "thumbnails"; 650395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_VIDEO_THUMBNAILS = "videothumbnails"; 651395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 652afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String IMAGE_COLUMNS = 653afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_size,_display_name,mime_type,title,date_added," + 654afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified,description,picasa_id,isprivate,latitude,longitude," + 655bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," + 656bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "width,height"; 657bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 658bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang private static final String IMAGE_COLUMNSv407 = 659bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "_data,_size,_display_name,mime_type,title,date_added," + 660bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "date_modified,description,picasa_id,isprivate,latitude,longitude," + 661afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name"; 662afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 663805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen private static final String AUDIO_COLUMNSv99 = 664afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_display_name,_size,mime_type,date_added," + 665afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified,title,title_key,duration,artist_id,composer,album_id," + 666afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 667afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bookmark"; 668afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 669805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen private static final String AUDIO_COLUMNSv100 = 670805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "_data,_display_name,_size,mime_type,date_added," + 671805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "date_modified,title,title_key,duration,artist_id,composer,album_id," + 672805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 673805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "bookmark,album_artist"; 674805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen 675957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang private static final String AUDIO_COLUMNSv405 = 676957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "_data,_display_name,_size,mime_type,date_added,is_drm," + 677957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "date_modified,title,title_key,duration,artist_id,composer,album_id," + 678957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 679957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "bookmark,album_artist"; 680957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 681afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String VIDEO_COLUMNS = 682afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_display_name,_size,mime_type,date_added,date_modified," + 683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title,duration,artist,album,resolution,description,isprivate,tags," + 684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "category,language,mini_thumb_data,latitude,longitude,datetaken," + 685bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," + 686bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "height"; 687bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 688bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang private static final String VIDEO_COLUMNSv407 = 689bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "_data,_display_name,_size,mime_type,date_added,date_modified," + 690bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "title,duration,artist,album,resolution,description,isprivate,tags," + 691bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "category,language,mini_thumb_data,latitude,longitude,datetaken," + 692afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_magic,bucket_id,bucket_display_name, bookmark"; 693afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 694afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified"; 695afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * This method takes care of updating all the tables in the database to the 698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * current version, creating them if necessary. 699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * This method can only update databases at schema 63 or higher, which was 700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * created August 1, 2008. Older database will be cleared and recreated. 701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db Database 702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param internal True if this is the internal media database 703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 70490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen private static void updateDatabase(Context context, SQLiteDatabase db, boolean internal, 705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int fromVersion, int toVersion) { 706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // sanity checks 70890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen int dbversion = getDatabaseVersion(context); 70990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen if (toVersion != dbversion) { 71090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + dbversion); 711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException(); 712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (fromVersion > toVersion) { 71395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion + 714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " to " + toVersion + ". Did you forget to wipe data?"); 715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException(); 716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 717988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen long startTime = SystemClock.currentTimeMicro(); 718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 719d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag. 720acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // We can't downgrade from those revisions, so start over. 721022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen // (the initial change to do this was wrong, so now we actually need to start over 722022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen // if the database version is 84-89) 723bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair. 724bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // However version 91 was reused in a divergent development path for gingerbread, 725bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // so we need to support upgrades from 91. 726bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Therefore we will only force a reset for versions 92 - 94. 727d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) || 728bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin (fromVersion >= 92 && fromVersion <= 94)) { 729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Drop everything and start over. 730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.i(TAG, "Upgrading media database from version " + 731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fromVersion + " to " + toVersion + ", which will destroy all old data"); 732d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen fromVersion = 63; 733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS images"); 734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS images_cleanup"); 735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS thumbnails"); 736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup"); 737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_meta"); 738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS artists"); 739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS albums"); 740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS album_art"); 741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS artist_info"); 742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS album_info"); 743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS artists_albums_map"); 744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_genres"); 746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_genres_map"); 747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup"); 748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_playlists"); 749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_playlists_map"); 750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup"); 751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1"); 752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2"); 753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS video"); 754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS video_cleanup"); 755cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood db.execSQL("DROP TABLE IF EXISTS objects"); 7569ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup"); 7579ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup"); 7589ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup"); 7599ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup"); 760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS images (" + 762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + 764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT," + 768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "description TEXT," + 771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "picasa_id TEXT," + 772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "isprivate INTEGER," + 773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "latitude DOUBLE," + 774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "longitude DOUBLE," + 775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "datetaken INTEGER," + 776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "orientation INTEGER," + 777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_magic INTEGER," + 778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "bucket_id TEXT," + 779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "bucket_display_name TEXT" + 780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);"); 783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " + 785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM thumbnails WHERE image_id = old._id;" + 787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 790b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // create image thumbnail table 791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" + 792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + 794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "image_id INTEGER," + 795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "kind INTEGER," + 796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "width INTEGER," + 797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "height INTEGER" + 798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);"); 801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " + 803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains meta data about audio files 808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" + 809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 810216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen "_data TEXT UNIQUE NOT NULL," + 811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT NOT NULL," + 817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title_key TEXT NOT NULL," + 818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "duration INTEGER," + 819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_id INTEGER," + 820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "composer TEXT," + 821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER," + 822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "track INTEGER," + // track is an integer to allow proper sorting 823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "year INTEGER CHECK(year!=0)," + 824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_ringtone INTEGER," + 825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_music INTEGER," + 826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_alarm INTEGER," + 827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_notification INTEGER" + 828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains a sort/group "key" and the preferred display name for artists 831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS artists (" + 832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_id INTEGER PRIMARY KEY," + 833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_key TEXT NOT NULL UNIQUE," + 834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist TEXT NOT NULL" + 835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains a sort/group "key" and the preferred display name for albums 838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS albums (" + 839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER PRIMARY KEY," + 840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_key TEXT NOT NULL UNIQUE," + 841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album TEXT NOT NULL" + 842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" + 845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER PRIMARY KEY," + 846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT" + 847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project recreateAudioView(db); 85095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides some extra info about artists, like the number of tracks 853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // and albums for this artist 854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " + 855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT artist_id AS _id, artist, artist_key, " + 856acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "COUNT(DISTINCT album) AS number_of_albums, " + 857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+ 858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "GROUP BY artist_key;"); 859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides extra info albums, such as the number of tracks 861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " + 862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT audio.album_id AS _id, album, album_key, " + 863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "MIN(year) AS minyear, " + 864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "MAX(year) AS maxyear, artist, artist_id, artist_key, " + 865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS + 866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ",album_art._data AS album_art" + 867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" + 868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " WHERE is_music=1 GROUP BY audio.album_id;"); 869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 870acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // For a given artist_id, provides the album_id for albums on 871acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // which the artist appears. 872acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " + 873acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "SELECT DISTINCT artist_id, album_id FROM audio_meta;"); 874acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /* 876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Only external media volumes can handle genres, playlists, etc. 877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!internal) { 879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio file is deleted 880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " + 881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_genres_map WHERE audio_id = old._id;" + 883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" + 884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains audio genre definitions 887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" + 888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "name TEXT NOT NULL" + 890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 89278b2885edc406273d688536b0eadfea006b20662Marco Nelissen // Contains mappings between audio genres and audio files 893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" + 894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_id INTEGER NOT NULL," + 896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "genre_id INTEGER NOT NULL" + 897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio genre is delete 900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " + 901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_genres_map WHERE genre_id = old._id;" + 903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains audio playlist definitions 906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" + 907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + // _data is path for file based playlists, or null 909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "name TEXT NOT NULL," + 910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER" + 912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains mappings between audio playlists and audio files 915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" + 916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_id INTEGER NOT NULL," + 918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "playlist_id INTEGER NOT NULL," + 919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "play_order INTEGER NOT NULL" + 920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio playlist is deleted 923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " + 924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up album_art table entry when an album is deleted 930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " + 931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM album_art WHERE album_id = old.album_id;" + 933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up album_art when an album is deleted 936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " + 937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains meta data about video files 943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS video (" + 944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT NOT NULL," + 946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT," + 952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "duration INTEGER," + 953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist TEXT," + 954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album TEXT," + 955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "resolution TEXT," + 956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "description TEXT," + 957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "isprivate INTEGER," + // for YouTube videos 958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "tags TEXT," + // for YouTube videos 959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "category TEXT," + // for YouTube videos 960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "language TEXT," + // for YouTube videos 961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_data TEXT," + 962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "latitude DOUBLE," + 963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "longitude DOUBLE," + 964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "datetaken INTEGER," + 965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_magic INTEGER" + 966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " + 969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // At this point the database is at least at schema version 63 (it was 975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // either created at version 63 by the code above, or was already at 976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // version 63 or later) 977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 64) { 979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the index that updates the database to schema version 64 980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);"); 981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 983acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 984acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.0 shipped with database version 64 985acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 986acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 65) { 988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the index that updates the database to schema version 65 989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);"); 990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 992403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // In version 66, originally we updateBucketNames(db, "images"), 993403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // but we need to do it in version 89 and therefore save the update here. 994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 67) { 996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the indices that update the database to schema version 67 997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);"); 998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);"); 999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 68) { 1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create bucket_id and bucket_display_name columns for the video table. 1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;"); 1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT"); 1005403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen 1006403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // In version 68, originally we updateBucketNames(db, "video"), 1007403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // but we need to do it in version 89 and therefore save the update here. 1008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 69) { 1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project updateDisplayName(db, "images"); 1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 70) { 1015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create bookmark column for the video table. 1016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;"); 1017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 101895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 71) { 1020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // There is no change to the database schema, however a code change 1021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // fixed parsing of metadata for certain files bought from the 1022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // iTunes music store, so we want to rescan files that might need it. 1023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // We do this by clearing the modification date in the database for 1024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // those files, so that the media scanner will see them as updated 1025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // and rescan them. 1026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" + 1027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _id FROM audio where mime_type='audio/mp4' AND " + 1028e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "artist='" + MediaStore.UNKNOWN_STRING + "' AND " + 1029e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "album='" + MediaStore.UNKNOWN_STRING + "'" + 1030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 1031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 103295ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 72) { 1034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create is_podcast and bookmark columns for the audio table. 1035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;"); 1036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';"); 1037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" + 1038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND _data NOT LIKE '%/music/%';"); 1039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;"); 1040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // New columns added to tables aren't visible in views on those tables 1042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // without opening and closing the database (or using the 'vacuum' command, 1043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // which we can't do here because all this code runs inside a transaction). 1044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // To work around this, we drop and recreate the affected view and trigger. 1045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project recreateAudioView(db); 1046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 104795ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1048acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1049acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.5 shipped with database version 72 1050acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1051acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 10528d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen if (fromVersion < 73) { 10538d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // There is no change to the database schema, but we now do case insensitive 10548d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // matching of folder names when determining whether something is music, a 10558d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // ringtone, podcast, etc, so we might need to reclassify some files. 10568d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " + 10578d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/music/%';"); 10588d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " + 10598d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/ringtones/%';"); 10608d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " + 10618d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/notifications/%';"); 10628d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " + 10638d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/alarms/%';"); 10648d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " + 10658d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/podcasts/%';"); 10668d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen } 1067a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 1068a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (fromVersion < 74) { 1069a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // This view is used instead of the audio view by the union below, to force 1070a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // sqlite to use the title_key index. This greatly reduces memory usage 1071a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // (no separate copy pass needed for sorting, which could cause errors on 1072a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // large datasets) and improves speed (by about 35% on a large dataset) 1073a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " + 1074a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "ORDER BY title_key;"); 1075a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 1076a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS search AS " + 1077a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT _id," + 1078a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'artist' AS mime_type," + 1079a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 1080a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS album," + 1081a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS title," + 1082a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text1," + 1083a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS text2," + 1084a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "number_of_albums AS data1," + 1085a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "number_of_tracks AS data2," + 1086a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key AS match," + 1087a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/artists/'||_id AS suggest_intent_data," + 1088a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "1 AS grouporder " + 1089e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " + 1090a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "UNION ALL " + 1091a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT _id," + 1092a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'album' AS mime_type," + 1093a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 1094a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album," + 1095a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS title," + 1096a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album AS text1," + 1097a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text2," + 1098a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data1," + 1099a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data2," + 1100a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key||' '||album_key AS match," + 1101a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/albums/'||_id AS suggest_intent_data," + 1102a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "2 AS grouporder " + 1103e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " + 1104a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "UNION ALL " + 1105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT searchhelpertitle._id AS _id," + 1106a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "mime_type," + 1107a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 1108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album," + 1109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "title," + 1110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "title AS text1," + 1111a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text2," + 1112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data1," + 1113a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data2," + 1114a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key||' '||album_key||' '||title_key AS match," + 1115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/media/'||searchhelpertitle._id AS " + 1116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "suggest_intent_data," + 1117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "3 AS grouporder " + 1118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "FROM searchhelpertitle WHERE (title != '') " 1119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ); 1120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } 112159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 112259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (fromVersion < 75) { 112395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen // Force a rescan of the audio entries so we can apply the new logic to 112459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // distinguish same-named albums. 112559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen db.execSQL("UPDATE audio_meta SET date_modified=0;"); 112659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen db.execSQL("DELETE FROM albums"); 112759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 112815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen 112915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen if (fromVersion < 76) { 113015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen // We now ignore double quotes when building the key, so we have to remove all of them 113115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen // from existing keys. 113215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE audio_meta SET title_key=" + 113315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(title_key,x'081D08C29F081D',x'081D') " + 113415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';"); 113515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE albums SET album_key=" + 113615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(album_key,x'081D08C29F081D',x'081D') " + 113715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';"); 113815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE artists SET artist_key=" + 113915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(artist_key,x'081D08C29F081D',x'081D') " + 114015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';"); 114115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen } 1142b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1143acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1144acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.6 shipped with database version 76 1145acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1146acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 1147b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (fromVersion < 77) { 1148b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // create video thumbnail table 1149b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" + 1150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "_id INTEGER PRIMARY KEY," + 1151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "_data TEXT," + 1152b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "video_id INTEGER," + 1153b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "kind INTEGER," + 1154b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "width INTEGER," + 1155b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "height INTEGER" + 1156b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ");"); 1157b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1158b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);"); 1159b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1160b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " + 1161b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "BEGIN " + 1162b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "SELECT _DELETE_FILE(old._data);" + 1163b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "END"); 1164b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 11651769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen 1166acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1167acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 2.0 and 2.0.1 shipped with database version 77 1168acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1169acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 11701769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen if (fromVersion < 78) { 1171044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen // Force a rescan of the video entries so we can update 11721769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen // latest changed DATE_TAKEN units (in milliseconds). 11731769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen db.execSQL("UPDATE video SET date_modified=0;"); 11741769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen } 1175268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen 1176acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1177acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 2.1 shipped with database version 78 1178acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1179acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 1180268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if (fromVersion < 79) { 1181268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // move /sdcard/albumthumbs to 1182268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // /sdcard/Android/data/com.android.providers.media/albumthumbs, 1183268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // and update the database accordingly 1184268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen 11859be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs"; 11869be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER; 1187268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen File thumbsfolder = new File(oldthumbspath); 1188268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if (thumbsfolder.exists()) { 1189268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // move folder to its new location 1190268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen File newthumbsfolder = new File(newthumbspath); 1191268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen newthumbsfolder.getParentFile().mkdirs(); 1192268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if(thumbsfolder.renameTo(newthumbsfolder)) { 1193268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // update the database 1194268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" + 1195268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen oldthumbspath + "','" + newthumbspath + "');"); 1196268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1197268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1198268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1199044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen 1200044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen if (fromVersion < 80) { 1201044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen // Force rescan of image entries to update DATE_TAKEN as UTC timestamp. 1202044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen db.execSQL("UPDATE images SET date_modified=0;"); 1203044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen } 12040ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 1205166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen if (fromVersion < 81 && !internal) { 12060ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Delete entries starting with /mnt/sdcard. This is for the benefit 12070ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // of users running builds between 2.0.1 and 2.1 final only, since 12080ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // users updating from 2.0 or earlier will not have such entries. 12090ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 12100ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // First we need to update the _data fields in the affected tables, since 12110ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // otherwise deleting the entries will also delete the underlying files 12120ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // (via a trigger), and we want to keep them. 12130ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12140ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12150ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12160ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12170ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12180ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 1219216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12200ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Once the paths have been renamed, we can safely delete the entries 12210ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';"); 12220ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM images WHERE _data IS '////';"); 12230ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM video WHERE _data IS '////';"); 12240ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';"); 12250ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';"); 12260ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM audio_meta WHERE _data IS '////';"); 12270ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM album_art WHERE _data IS '////';"); 12280ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 12290ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // rename existing entries starting with /sdcard to /mnt/sdcard 12300ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_meta" + 12310ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12320ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_playlists" + 12330ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12340ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE images" + 12350ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12360ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE video" + 12370ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12380ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE videothumbnails" + 12390ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12400ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE thumbnails" + 12410ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12420ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE album_art" + 12430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12440ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 12450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Delete albums and artists, then clear the modification time on songs, which 12460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // will cause the media scanner to rescan everything, rebuilding the artist and 12470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // album tables along the way, while preserving playlists. 12480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // We need this rescan because ICU also changed, and now generates different 12490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // collation keys 12500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE from albums"); 12510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE from artists"); 12520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_meta SET date_modified=0;"); 12530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen } 125484403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen 125584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen if (fromVersion < 82) { 1256acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // recreate this view with the correct "group by" specifier 125784403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen db.execSQL("DROP VIEW IF EXISTS artist_info"); 125884403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " + 125984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "SELECT artist_id AS _id, artist, artist_key, " + 1260acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "COUNT(DISTINCT album_key) AS number_of_albums, " + 126184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+ 126284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "GROUP BY artist_key;"); 126384403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen } 1264216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen 1265acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* we skipped over version 83, and reverted versions 84, 85 and 86 */ 1266ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen 1267ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen if (fromVersion < 87) { 1268ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // The fastscroll thumb needs an index on the strings being displayed, 1269ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // otherwise the queries it does to determine the correct position 1270ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // becomes really inefficient 1271022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);"); 1272022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);"); 1273022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);"); 1274ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen } 1275216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen 1276acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen if (fromVersion < 88) { 1277acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // Clean up a few more things from versions 84/85/86, and recreate 1278acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // the few things worth keeping from those changes. 1279acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update1;"); 1280acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update2;"); 1281acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update3;"); 1282acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update4;"); 1283acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update1;"); 1284acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update2;"); 1285acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update3;"); 1286acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update4;"); 128716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP VIEW IF EXISTS album_artists;"); 1288acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);"); 1289acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);"); 1290acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // For a given artist_id, provides the album_id for albums on 1291acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // which the artist appears. 1292acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " + 1293acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "SELECT DISTINCT artist_id, album_id FROM audio_meta;"); 1294acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen } 1295403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen 1296fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // In version 89, originally we updateBucketNames(db, "images") and 1297fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames 1298fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // for all files and therefore can save the update here. 1299b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 1300b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (fromVersion < 91) { 1301bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Never query by mini_thumb_magic_index 1302bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index"); 1303bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin 1304bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // sort the items by taken date in each bucket 1305bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)"); 1306bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)"); 1307bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin } 1308bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin 1309a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu 1310d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // Gingerbread ended up going to version 100, but didn't yet have the "files" 1311d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // table, so we need to create that if we're at 100 or lower. This means 1312d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // we won't be able to upgrade pre-release Honeycomb. 1313d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen if (fromVersion <= 100) { 1314afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Remove various stages of work in progress for MTP support 1315afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS objects"); 1316afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS files"); 131716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;"); 131816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;"); 131916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;"); 132016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;"); 1321afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;"); 1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;"); 1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;"); 1324afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;"); 1325afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;"); 1326afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1327afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Create a new table to manage all files in our storage. 1328afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // This contains a union of all the columns from the old 1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // images, audio_meta, videos and audio_playlist tables. 1330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TABLE files (" + 133100a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data TEXT," + // this can be null for playlists 1333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_size INTEGER," + 1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "format INTEGER," + 1335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "parent INTEGER," + 1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_added INTEGER," + 1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified INTEGER," + 1338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mime_type TEXT," + 1339afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title TEXT," + 1340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "description TEXT," + 1341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_display_name TEXT," + 1342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for images 1344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "picasa_id TEXT," + 1345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "orientation INTEGER," + 1346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for images and video 1348afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "latitude DOUBLE," + 1349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "longitude DOUBLE," + 1350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "datetaken INTEGER," + 1351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_magic INTEGER," + 1352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bucket_id TEXT," + 1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bucket_display_name TEXT," + 1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "isprivate INTEGER," + 1355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1356afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for audio 1357afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title_key TEXT," + 1358afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "artist_id INTEGER," + 1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "album_id INTEGER," + 1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "composer TEXT," + 1361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "track INTEGER," + 1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "year INTEGER CHECK(year!=0)," + 1363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_ringtone INTEGER," + 1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_music INTEGER," + 1365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_alarm INTEGER," + 1366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_notification INTEGER," + 1367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_podcast INTEGER," + 1368d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen "album_artist TEXT," + 1369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1370afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for audio and video 1371afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "duration INTEGER," + 1372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bookmark INTEGER," + 1373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for video 1375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "artist TEXT," + 1376afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "album TEXT," + 1377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "resolution TEXT," + 1378afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "tags TEXT," + 1379afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "category TEXT," + 1380afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "language TEXT," + 1381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_data TEXT," + 1382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for playlists 1384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "name TEXT," + 1385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // media_type is used by the views to emulate the old 1387afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // images, audio_meta, videos and audio_playlist tables. 1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "media_type INTEGER," + 1389afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1390afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Value of _id from the old media table. 1391afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Used only for updating other tables during database upgrade. 1392afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "old_id INTEGER" + 1393afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood ");"); 1394d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen 1395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX path_index ON files(_data);"); 1396afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX media_type_index ON files(media_type);"); 1397afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1398afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Copy all data from our obsolete tables to the new files table 139992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood 140092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // Copy audio records first, preserving the _id column. 140192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // We do this to maintain compatibility for content Uris for ringtones. 140292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // Unfortunately we cannot do this for images and videos as well. 140392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // We choose to do this for the audio table because the fragility of Uris 140492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // for ringtones are the most common problem we need to avoid. 140592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" + 140692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO + 140792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood " FROM audio_meta;"); 140892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood 1409bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT " 1410bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;"); 1411bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT " 1412bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;"); 141316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood if (!internal) { 1414afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT " 1415afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST 1416afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " FROM audio_playlists;"); 1417afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 141816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood 1419afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Delete the old tables 1420afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS images"); 1421afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS audio_meta"); 1422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS video"); 1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS audio_playlists"); 142416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood 1425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Create views to replace our old tables 1426bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 + 1427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_IMAGE + ";"); 1429d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 + 1430d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1431d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1432bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 + 1433afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1434afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_VIDEO + ";"); 1435afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS + 1437afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1438afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_PLAYLIST + ";"); 143916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood } 144036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 14419491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen // create temporary index to make the updates go faster 14429491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen db.execSQL("CREATE INDEX tmp ON files(old_id);"); 14439491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen 1444afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update the image_id column in the thumbnails table. 1445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files " 1446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = thumbnails.image_id AND files.media_type = " 1447afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_IMAGE + ");"); 144836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1449afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1450d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // update audio_id in the audio_genres_map table, and 1451d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // audio_playlists_map tables and playlist_id in the audio_playlists_map table 1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files " 1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = " 1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_AUDIO + ");"); 1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files " 1456afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = audio_playlists_map.audio_id " 1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");"); 1458d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files " 1459d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen + "WHERE files.old_id = audio_playlists_map.playlist_id " 1460d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");"); 1461afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 1462afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update video_id in the videothumbnails table. 1464afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files " 1465afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = " 1466afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_VIDEO + ");"); 1467afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 14689491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen // we don't need this index anymore now 14699491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen db.execSQL("DROP INDEX tmp;"); 14709491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen 1471afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update indices to work on the files table 1472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS title_idx"); 1473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS album_id_idx"); 1474afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS image_bucket_index"); 1475afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS video_bucket_index"); 1476afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS sort_index"); 1477afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS titlekey_index"); 1478afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS artist_id_idx"); 1479afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX title_idx ON files(title);"); 1480afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX album_id_idx ON files(album_id);"); 1481afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);"); 1482afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);"); 1483afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX titlekey_index ON files(title_key);"); 1484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);"); 1485afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1486afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Recreate triggers for our obsolete tables on the new files table 1487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_cleanup"); 1488afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 1489afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_cleanup"); 1490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup"); 1491afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_delete"); 149236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1493afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " + 1494afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " + 149536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1496afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM thumbnails WHERE image_id = old._id;" + 1497afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 149836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 149936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1500afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " + 150149dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " + 150236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1503afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 150436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 150536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1506afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1507afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " + 1508afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " + 1509afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "BEGIN " + 1510afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_genres_map WHERE audio_id = old._id;" + 1511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" + 1512afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "END"); 1513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1514afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " + 1515afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " + 1516afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "BEGIN " + 1517afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 1518afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 1519afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "END"); 1520afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1521afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " + 152236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1523afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from files where _id=old._id;" + 1524afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from audio_playlists_map where audio_id=old._id;" + 1525afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from audio_genres_map where audio_id=old._id;" + 152636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 152736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood } 152836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood } 152936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1530db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin if (fromVersion < 301) { 1531db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("DROP INDEX IF EXISTS bucket_index"); 1532db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)"); 1533db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)"); 1534db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin } 1535db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin 153620405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood if (fromVersion < 302) { 153720405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood db.execSQL("CREATE INDEX parent_index ON files(parent);"); 153820405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood db.execSQL("CREATE INDEX format_index ON files(format);"); 153920405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood } 154020405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood 15412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (fromVersion < 303) { 15422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // the album disambiguator hash changed, so rescan songs and force 15432658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // albums to be updated. Artists are unaffected. 15442658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen db.execSQL("DELETE from albums"); 15452658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 15462658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 15472658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } 15482658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 15494b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood if (fromVersion < 304 && !internal) { 155051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood // notifies host when files are deleted 155151d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " + 155251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "BEGIN " + 155351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "SELECT _OBJECT_REMOVED(old._id);" + 155451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "END"); 155551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood 155651d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood } 155751d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood 15584b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood if (fromVersion < 305 && internal) { 15594b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood // version 304 erroneously added this trigger to the internal database 15604b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup"); 15614b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood } 15624b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood 1563fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen if (fromVersion < 306 && !internal) { 1564efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen // The genre list was expanded and genre string parsing was tweaked, so 1565efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen // rebuild the genre list 1566efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 1567efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1568efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("DELETE FROM audio_genres_map"); 1569efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("DELETE FROM audio_genres"); 1570efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen } 1571efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen 157265587f9c204abb2d179527dfdca009f4780e9743Ray Chen if (fromVersion < 307 && !internal) { 157365587f9c204abb2d179527dfdca009f4780e9743Ray Chen // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or 157465587f9c204abb2d179527dfdca009f4780e9743Ray Chen // EXIF local time. 157565587f9c204abb2d179527dfdca009f4780e9743Ray Chen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 157665587f9c204abb2d179527dfdca009f4780e9743Ray Chen + FileColumns.MEDIA_TYPE_IMAGE + ";"); 157765587f9c204abb2d179527dfdca009f4780e9743Ray Chen } 157865587f9c204abb2d179527dfdca009f4780e9743Ray Chen 1579e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // Honeycomb went up to version 307, ICS started at 401 1580e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 15814f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood // Database version 401 did not add storage_id to the internal database. 15824f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood // We need it there too, so add it in version 402 15834f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood if (fromVersion < 401 || (fromVersion == 401 && internal)) { 15849be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood // Add column for MTP storage ID 15859be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;"); 15869be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood // Anything in the database before this upgrade step will be in the primary storage 15879be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";"); 15889be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 15899be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood 159078b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (fromVersion < 403 && !internal) { 159178b2885edc406273d688536b0eadfea006b20662Marco Nelissen db.execSQL("CREATE VIEW audio_genres_map_noid AS " + 159278b2885edc406273d688536b0eadfea006b20662Marco Nelissen "SELECT audio_id,genre_id from audio_genres_map;"); 159378b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 159478b2885edc406273d688536b0eadfea006b20662Marco Nelissen 15959289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (fromVersion < 404) { 15969289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen // There was a bug that could cause distinct same-named albums to be 15979289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen // combined again. Delete albums and force a rescan. 15989289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen db.execSQL("DELETE from albums"); 15999289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 16009289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 16019289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 16029289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen 1603957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang if (fromVersion < 405) { 1604957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang // Add is_drm column. 1605957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;"); 1606957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 1607957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang db.execSQL("DROP VIEW IF EXISTS audio_meta"); 1608957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 + 1609957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1610957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1611957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 1612957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang recreateAudioView(db); 1613957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang } 1614957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 1615b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (fromVersion < 407) { 16167ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang // Rescan files in the media database because a new column has been added 1617b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // in table files in version 405 and to recover from problems populating 1618b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // the genre tables 16197ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang db.execSQL("UPDATE files SET date_modified=0;"); 16207ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang } 16217ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang 1622bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang if (fromVersion < 408) { 1623bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang // Add the width/height columns for images and video 1624bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;"); 1625bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;"); 1626bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 1627bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang // Rescan files to fill the columns 1628bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("UPDATE files SET date_modified=0;"); 1629bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 1630bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang // Update images and video views to contain the width/height columns 1631bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("DROP VIEW IF EXISTS images"); 1632bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("DROP VIEW IF EXISTS video"); 1633bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS + 1634bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1635bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + FileColumns.MEDIA_TYPE_IMAGE + ";"); 1636bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS + 1637bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1638bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + FileColumns.MEDIA_TYPE_VIDEO + ";"); 1639bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang } 1640bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 16415809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen if (fromVersion < 409 && !internal) { 16425809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen // A bug that prevented numeric genres from being parsed was fixed, so 16435809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen // rebuild the genre list 16445809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 16455809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 16465809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen db.execSQL("DELETE FROM audio_genres_map"); 16475809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen db.execSQL("DELETE FROM audio_genres"); 16485809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen } 16495809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen 1650e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // ICS went out with database version 409, JB started at 500 1651e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1652166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen if (fromVersion < 500) { 1653166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen // we're now deleting the file in mediaprovider code, rather than via a trigger 1654166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;"); 1655166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } 1656a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen if (fromVersion < 501) { 1657a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // we're now deleting the file in mediaprovider code, rather than via a trigger 1658a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // the images_cleanup trigger would delete the image file and the entry 1659a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // in the thumbnail table, which in turn would trigger thumbnails_cleanup 1660a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // to delete the thumbnail image 1661a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS images_cleanup;"); 1662a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup;"); 1663a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 16645afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen if (fromVersion < 502) { 16655afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen // we're now deleting the file in mediaprovider code, rather than via a trigger 16665afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS video_cleanup;"); 16675afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen } 16685118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen if (fromVersion < 503) { 16695118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen // genre and playlist cleanup now done in mediaprovider code, instead of in a trigger 16705118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS audio_delete"); 16715118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 16725118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen } 167390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen if (fromVersion < 504) { 167490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen // add an index to help with case-insensitive matching of paths 167590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen db.execSQL( 167690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen "CREATE INDEX IF NOT EXISTS path_index_lower ON files(_data COLLATE NOCASE);"); 167790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } 16787074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen if (fromVersion < 505) { 16797074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen // Starting with schema 505 we fill in the width/height/resolution columns for videos, 16807074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen // so force a rescan of videos to fill in the blanks 16817074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 16827074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen + FileColumns.MEDIA_TYPE_VIDEO + ";"); 16837074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen } 168459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen if (fromVersion < 506) { 168559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // sd card storage got moved to /storage/sdcard0 168659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // first delete everything that already got scanned in /storage before this 168759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // update step was added 168859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS files_cleanup"); 168959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM files WHERE _data LIKE '/storage/%';"); 169059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM album_art WHERE _data LIKE '/storage/%';"); 169159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM thumbnails WHERE _data LIKE '/storage/%';"); 169259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM videothumbnails WHERE _data LIKE '/storage/%';"); 169359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // then rename everything from /mnt/sdcard/ to /storage/sdcard0, 169459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // and from /mnt/external1 to /storage/sdcard1 169559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE files SET " + 169659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 169759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE files SET " + 169859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 169959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE album_art SET " + 170059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 170159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE album_art SET " + 170259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 170359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE thumbnails SET " + 170459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 170559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE thumbnails SET " + 170659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 170759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE videothumbnails SET " + 170859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 170959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE videothumbnails SET " + 171059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 171159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen 171259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen if (!internal) { 171359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " + 171459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "BEGIN " + 171559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "SELECT _OBJECT_REMOVED(old._id);" + 171659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "END"); 171759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen } 171859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen } 1719a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin if (fromVersion < 507) { 1720a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin // we update _data in version 506, we need to update the bucket_id as well 1721677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen updateBucketNames(db); 1722a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin } 172346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen if (fromVersion < 508 && !internal) { 172446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen // ensure we don't get duplicate entries in the genre map 172546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map_tmp (" + 172646e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "_id INTEGER PRIMARY KEY," + 172746e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "audio_id INTEGER NOT NULL," + 172846e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "genre_id INTEGER NOT NULL," + 172946e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "UNIQUE (audio_id,genre_id) ON CONFLICT IGNORE" + 173046e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen ");"); 173146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("INSERT INTO audio_genres_map_tmp (audio_id,genre_id)" + 173246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen " SELECT DISTINCT audio_id,genre_id FROM audio_genres_map;"); 173346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("DROP TABLE audio_genres_map;"); 173446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("ALTER TABLE audio_genres_map_tmp RENAME TO audio_genres_map;"); 173546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen } 1736988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 1737988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (fromVersion < 509) { 1738988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen db.execSQL("CREATE TABLE IF NOT EXISTS log (time DATETIME PRIMARY KEY, message TEXT);"); 1739988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 1740395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 1741395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey // Emulated external storage moved to user-specific paths 1742395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey if (fromVersion < 510 && Environment.isExternalStorageEmulated()) { 1743395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey // File.fixSlashes() removes any trailing slashes 1744395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey final String externalStorage = Environment.getExternalStorageDirectory().toString(); 174557b65f10aaafc4bf42a0fa59eb2bbe6c8371c2e6Jeff Sharkey Log.d(TAG, "Adjusting external storage paths to: " + externalStorage); 1746395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 1747395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey final String[] tables = { 1748395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey TABLE_FILES, TABLE_ALBUM_ART, TABLE_THUMBNAILS, TABLE_VIDEO_THUMBNAILS }; 1749395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey for (String table : tables) { 1750395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey db.execSQL("UPDATE " + table + " SET " + "_data='" + externalStorage 1751395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey + "'||SUBSTR(_data,17) WHERE _data LIKE '/storage/sdcard0/%';"); 1752395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey } 1753395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey } 17548bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey if (fromVersion < 511) { 17558bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey // we update _data in version 510, we need to update the bucket_id as well 17568bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey updateBucketNames(db); 17578bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey } 1758395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 1759e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // JB 4.2 went out with database version 511, starting next release with 600 1760e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1761e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen if (fromVersion < 600) { 1762e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // modify _data column to be unique and collate nocase. Because this drops the original 1763e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // table and replaces it with a new one by the same name, we need to also recreate all 1764e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // indices and triggers that refer to the files table. 1765e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // Views don't need to be recreated. 1766e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1767e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE TABLE files2 (_id INTEGER PRIMARY KEY AUTOINCREMENT," + 1768e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "_data TEXT UNIQUE" + 1769e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // the internal filesystem is case-sensitive 1770e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen (internal ? "," : " COLLATE NOCASE,") + 1771e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "_size INTEGER,format INTEGER,parent INTEGER,date_added INTEGER," + 1772e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "date_modified INTEGER,mime_type TEXT,title TEXT,description TEXT," + 1773e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "_display_name TEXT,picasa_id TEXT,orientation INTEGER,latitude DOUBLE," + 1774e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER,bucket_id TEXT," + 1775e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "bucket_display_name TEXT,isprivate INTEGER,title_key TEXT,artist_id INTEGER," + 1776e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "album_id INTEGER,composer TEXT,track INTEGER,year INTEGER CHECK(year!=0)," + 1777e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "is_ringtone INTEGER,is_music INTEGER,is_alarm INTEGER," + 1778e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "is_notification INTEGER,is_podcast INTEGER,album_artist TEXT," + 1779e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "duration INTEGER,bookmark INTEGER,artist TEXT,album TEXT,resolution TEXT," + 1780e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,name TEXT," + 1781e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "media_type INTEGER,old_id INTEGER,storage_id INTEGER,is_drm INTEGER," + 1782e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "width INTEGER, height INTEGER);"); 1783e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1784e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // copy data from old table, squashing entries with duplicate _data 1785e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("INSERT OR REPLACE INTO files2 SELECT * FROM files;"); 1786e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("DROP TABLE files;"); 1787e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("ALTER TABLE files2 RENAME TO files;"); 1788e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1789e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // recreate indices and triggers 1790e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX album_id_idx ON files(album_id);"); 1791e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);"); 1792e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX bucket_index on files(bucket_id,media_type," + 1793e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "datetaken, _id);"); 1794e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX bucket_name on files(bucket_id,media_type," + 1795e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "bucket_display_name);"); 1796e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX format_index ON files(format);"); 1797e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX media_type_index ON files(media_type);"); 1798e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX parent_index ON files(parent);"); 1799e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX path_index ON files(_data);"); 1800e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);"); 1801e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX title_idx ON files(title);"); 1802e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX titlekey_index ON files(title_key);"); 1803e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen if (!internal) { 1804e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE TRIGGER audio_playlists_cleanup DELETE ON files" + 1805e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen " WHEN old.media_type=4" + 1806e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen " BEGIN DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 1807e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "SELECT _DELETE_FILE(old._data);END;"); 1808e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE TRIGGER files_cleanup DELETE ON files" + 1809e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen " BEGIN SELECT _OBJECT_REMOVED(old._id);END;"); 1810e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen } 1811e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen } 1812e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1813f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen if (fromVersion < 601) { 1814f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen // remove primary key constraint because column time is not necessarily unique 1815f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("CREATE TABLE IF NOT EXISTS log_tmp (time DATETIME, message TEXT);"); 1816f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("DELETE FROM log_tmp;"); 1817f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("INSERT INTO log_tmp SELECT time, message FROM log order by rowid;"); 1818f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("DROP TABLE log;"); 1819f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("ALTER TABLE log_tmp RENAME TO log;"); 1820f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen } 1821f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen 1822acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sanityCheck(db, fromVersion); 1823988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000; 1824988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion 1825988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen + " in " + elapsedSeconds + " seconds"); 1826988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 1827988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 1828988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen /** 1829988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen * Write a persistent diagnostic message to the log table. 1830988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen */ 1831988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen static void logToDb(SQLiteDatabase db, String message) { 1832934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen db.execSQL("INSERT OR REPLACE" + 1833934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen " INTO log (time,message) VALUES (strftime('%Y-%m-%d %H:%M:%f','now'),?);", 1834988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen new String[] { message }); 1835988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen // delete all but the last 500 rows 1836988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen db.execSQL("DELETE FROM log WHERE rowid IN" + 1837f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen " (SELECT rowid FROM log ORDER BY rowid DESC LIMIT 500,-1);"); 18381d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen } 18391d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen 18401d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen /** 1841216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen * Perform a simple sanity check on the database. Currently this tests 1842216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen * whether all the _data entries in audio_meta are unique 1843216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen */ 1844216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen private static void sanityCheck(SQLiteDatabase db, int fromVersion) { 1845216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Cursor c1 = db.query("audio_meta", new String[] {"count(*)"}, 1846216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen null, null, null, null, null); 1847216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"}, 1848216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen null, null, null, null, null); 1849216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c1.moveToFirst(); 1850216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c2.moveToFirst(); 1851216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen int num1 = c1.getInt(0); 1852216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen int num2 = c2.getInt(0); 1853216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c1.close(); 1854216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c2.close(); 1855216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen if (num1 != num2) { 1856216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Log.e(TAG, "audio_meta._data column is not unique while upgrading" + 1857216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen " from schema " +fromVersion + " : " + num1 +"/" + num2); 1858216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen // Delete all audio_meta rows so they will be rebuilt by the media scanner 1859216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen db.execSQL("DELETE FROM audio_meta;"); 1860216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } 1861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void recreateAudioView(SQLiteDatabase db) { 1864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides a unified audio/artist/album info view. 1865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS audio"); 1866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " + 1867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " + 1868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;"); 1869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 187095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1872677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen * Update the bucket_id and bucket_display_name columns for images and videos 1873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db 1874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param tableName 1875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1876677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen private static void updateBucketNames(SQLiteDatabase db) { 1877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Rebuild the bucket_display_name column using the natural case rather than lower case. 1878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 1879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] columns = {BaseColumns._ID, MediaColumns.DATA}; 1881677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen // update only images and videos 1882677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen Cursor cursor = db.query("files", columns, "media_type=1 OR media_type=3", 1883677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen null, null, null, null); 1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID); 1886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA); 18879491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen String [] rowId = new String[1]; 1888988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen ContentValues values = new ContentValues(); 1889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor.moveToNext()) { 1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = cursor.getString(dataColumnIndex); 1891988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen rowId[0] = cursor.getString(idColumnIndex); 1892d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen if (data != null) { 1893988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen values.clear(); 1894d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen computeBucketValues(data, values); 1895677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen db.update("files", values, "_id=?", rowId); 1896d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen } else { 1897d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen Log.w(TAG, "null data at id " + rowId); 1898d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen } 1899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project cursor.close(); 1902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 1904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 1906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Iterate through the rows of a table in a database, ensuring that the 1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * display name column has a value. 1912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db 1913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param tableName 1914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void updateDisplayName(SQLiteDatabase db, String tableName) { 1916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Fill in default values for null displayName values 1917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 1918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME}; 1920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor cursor = db.query(tableName, columns, null, null, null, null, null); 1921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID); 1923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA); 1924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME); 1925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(); 1926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor.moveToNext()) { 1927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String displayName = cursor.getString(displayNameIndex); 1928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (displayName == null) { 1929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = cursor.getString(dataColumnIndex); 1930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.clear(); 1931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project computeDisplayName(data, values); 1932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int rowId = cursor.getInt(idColumnIndex); 1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.update(tableName, values, "_id=" + rowId, null); 1934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project cursor.close(); 1938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 1942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1944988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param data The input path 1947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param values the content values, where the bucked id name and bucket display name are updated. 1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void computeBucketValues(String data, ContentValues values) { 1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File parentFile = new File(data).getParentFile(); 1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (parentFile == null) { 1953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project parentFile = new File("/"); 1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Lowercase the path for hashing. This avoids duplicate buckets if the 1957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // filepath case is changed externally. 1958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Keep the original case for display. 1959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String path = parentFile.toString().toLowerCase(); 1960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name = parentFile.getName(); 1961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the 1963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // same for both images and video. However, for backwards-compatibility reasons 1964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // there is no common base class. We use the ImageColumns version here 1965d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood values.put(ImageColumns.BUCKET_ID, path.hashCode()); 1966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(ImageColumns.BUCKET_DISPLAY_NAME, name); 1967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param data The input path 1971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param values the content values, where the display name is updated. 1972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 1973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void computeDisplayName(String data, ContentValues values) { 1975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = (data == null ? "" : data.toString()); 1976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int idx = s.lastIndexOf('/'); 1977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (idx >= 0) { 1978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project s = s.substring(idx + 1); 1979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("_display_name", s); 1981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1983b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen /** 1984498b62c2912302a23532c73a028a7684c5df33caRay Chen * Copy taken time from date_modified if we lost the original value (e.g. after factory reset) 1985498b62c2912302a23532c73a028a7684c5df33caRay Chen * This works for both video and image tables. 1986b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen * 1987b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen * @param values the content values, where taken time is updated. 1988b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen */ 1989b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen private static void computeTakenTime(ContentValues values) { 1990b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen if (! values.containsKey(Images.Media.DATE_TAKEN)) { 1991b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen // This only happens when MediaScanner finds an image file that doesn't have any useful 1992b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen // reference to get this value. (e.g. GPSTimeStamp) 1993498b62c2912302a23532c73a028a7684c5df33caRay Chen Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED); 1994b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen if (lastModified != null) { 1995b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen values.put(Images.Media.DATE_TAKEN, lastModified * 1000); 1996b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 1997b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 1998b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 1999b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen 2000b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen /** 2001b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * This method blocks until thumbnail is ready. 2002b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * 2003b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * @param thumbUri 2004b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * @return 2005b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen */ 2006b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private boolean waitForThumbnailReady(Uri origUri) { 2007b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA, 2008b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ImageColumns.MINI_THUMB_MAGIC}, null, null, null); 2009b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c == null) return false; 2010b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2011b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean result = false; 2012b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2013b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c.moveToFirst()) { 2014e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen long id = c.getLong(0); 2015b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String path = c.getString(1); 2016b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen long magic = c.getLong(2); 2017b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 20189299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen MediaThumbRequest req = requestMediaThumbnail(path, origUri, 20199299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen MediaThumbRequest.PRIORITY_HIGH, magic); 20209299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen if (req == null) { 20219299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen return false; 20229299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } 20239299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen synchronized (req) { 20249299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen try { 20259299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen while (req.mState == MediaThumbRequest.State.WAIT) { 20269299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen req.wait(); 202720434e032e498b716f87cce2f23dd646819218bfRay Chen } 20289299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } catch (InterruptedException e) { 20299299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen Log.w(TAG, e); 20309299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } 20319299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen if (req.mState == MediaThumbRequest.State.DONE) { 20329299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen result = true; 2033b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2034b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2035b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2036b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen c.close(); 2037b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2038b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return result; 2039b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2040b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2041e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid, 2042e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean isVideo) { 2043e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean cancelAllOrigId = (id == -1); 2044e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean cancelAllGroupId = (gid == -1); 2045e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen return (req.mCallingPid == pid) && 2046e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (cancelAllGroupId || req.mGroupId == gid) && 2047e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (cancelAllOrigId || req.mOrigId == id) && 2048e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (req.mIsVideo == isVideo); 2049e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } 2050e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 2051b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table, 2052b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String column, boolean hasThumbnailId) { 2053b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.setTables(table); 2054b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (hasThumbnailId) { 2055b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // For uri dispatched to this method, the 4th path segment is always 2056b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // the thumbnail id. 2057b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.appendWhere("_id = " + uri.getPathSegments().get(3)); 2058b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // client already knows which thumbnail it wants, bypass it. 2059b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 2060b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2061b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String origId = uri.getQueryParameter("orig_id"); 2062b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // We can't query ready_flag unless we know original id 2063b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (origId == null) { 2064b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // this could be thumbnail query for other purpose, bypass it. 2065b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 2066b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2067b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2068b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean needBlocking = "1".equals(uri.getQueryParameter("blocking")); 206920434e032e498b716f87cce2f23dd646819218bfRay Chen boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel")); 2070e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Uri origUri = uri.buildUpon().encodedPath( 2071e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen uri.getPath().replaceFirst("thumbnails", "media")) 2072e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen .appendPath(origId).build(); 2073b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2074b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (needBlocking && !waitForThumbnailReady(origUri)) { 207520434e032e498b716f87cce2f23dd646819218bfRay Chen Log.w(TAG, "original media doesn't exist or it's canceled."); 2076b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return false; 207720434e032e498b716f87cce2f23dd646819218bfRay Chen } else if (cancelRequest) { 2078e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen String groupId = uri.getQueryParameter("group_id"); 2079e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean isVideo = "video".equals(uri.getPathSegments().get(1)); 208020434e032e498b716f87cce2f23dd646819218bfRay Chen int pid = Binder.getCallingPid(); 208120434e032e498b716f87cce2f23dd646819218bfRay Chen long id = -1; 2082e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen long gid = -1; 2083e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 208420434e032e498b716f87cce2f23dd646819218bfRay Chen try { 208520434e032e498b716f87cce2f23dd646819218bfRay Chen id = Long.parseLong(origId); 2086e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen gid = Long.parseLong(groupId); 208720434e032e498b716f87cce2f23dd646819218bfRay Chen } catch (NumberFormatException ex) { 208820434e032e498b716f87cce2f23dd646819218bfRay Chen // invalid cancel request 208920434e032e498b716f87cce2f23dd646819218bfRay Chen return false; 209020434e032e498b716f87cce2f23dd646819218bfRay Chen } 2091e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 209220434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mMediaThumbQueue) { 2093e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen if (mCurrentThumbRequest != null && 2094e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) { 209520434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mCurrentThumbRequest) { 209620434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL; 209720434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.notifyAll(); 209820434e032e498b716f87cce2f23dd646819218bfRay Chen } 209920434e032e498b716f87cce2f23dd646819218bfRay Chen } 210020434e032e498b716f87cce2f23dd646819218bfRay Chen for (MediaThumbRequest mtq : mMediaThumbQueue) { 2101e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen if (matchThumbRequest(mtq, pid, id, gid, isVideo)) { 210220434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mtq) { 210320434e032e498b716f87cce2f23dd646819218bfRay Chen mtq.mState = MediaThumbRequest.State.CANCEL; 210420434e032e498b716f87cce2f23dd646819218bfRay Chen mtq.notifyAll(); 210520434e032e498b716f87cce2f23dd646819218bfRay Chen } 210620434e032e498b716f87cce2f23dd646819218bfRay Chen 210720434e032e498b716f87cce2f23dd646819218bfRay Chen mMediaThumbQueue.remove(mtq); 210820434e032e498b716f87cce2f23dd646819218bfRay Chen } 210920434e032e498b716f87cce2f23dd646819218bfRay Chen } 211020434e032e498b716f87cce2f23dd646819218bfRay Chen } 2111b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2112b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2113b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (origId != null) { 2114b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.appendWhere(column + " = " + origId); 2115b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2116b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 2117b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2118b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen @SuppressWarnings("fallthrough") 2119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public Cursor query(Uri uri, String[] projectionIn, String selection, 2121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] selectionArgs, String sort) { 2122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int table = URI_MATCHER.match(uri); 2123baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen List<String> prependArgs = new ArrayList<String>(); 2124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 212501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // Log.v(TAG, "query: uri="+uri+", selection="+selection); 2126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 2127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (table == MEDIA_SCANNER) { 2128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mMediaScannerVolume == null) { 2129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 2130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 2131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create a cursor to return volume currently being scanned by the media scanner 21320027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME}); 21330027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen c.addRow(new String[] {mMediaScannerVolume}); 21340027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen return c; 2135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 21380027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // Used temporarily (until we have unique media IDs) to get an identifier 21390027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // for the current sd card, so that the music app doesn't have to use the 21400027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // non-public getFatVolumeId method 21410027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen if (table == FS_ID) { 21420027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen MatrixCursor c = new MatrixCursor(new String[] {"fsid"}); 21430027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen c.addRow(new Integer[] {mVolumeId}); 21440027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen return c; 21450027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen } 21460027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 2147704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen if (table == VERSION) { 2148704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen MatrixCursor c = new MatrixCursor(new String[] {"version"}); 214990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen c.addRow(new Integer[] {getDatabaseVersion(getContext())}); 2150704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen return c; 2151704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen } 2152704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen 2153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String groupBy = null; 215410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 215510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null) { 2156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 2157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 215810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 215910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getReadableDatabase(); 21605fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) return null; 2161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 21624574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String limit = uri.getQueryParameter("limit"); 2163c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String filter = uri.getQueryParameter("filter"); 2164c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String [] keywords = null; 2165c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (filter != null) { 2166c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen filter = Uri.decode(filter).trim(); 2167c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (!TextUtils.isEmpty(filter)) { 2168c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String [] searchWords = filter.split(" "); 2169c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen keywords = new String[searchWords.length]; 2170c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; i < searchWords.length; i++) { 2171c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String key = MediaStore.Audio.keyFor(searchWords[i]); 2172c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("\\", "\\\\"); 2173c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("%", "\\%"); 2174c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("_", "\\_"); 2175c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen keywords[i] = key; 2176c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2177c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2178c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2179db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin if (uri.getQueryParameter("distinct") != null) { 2180db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin qb.setDistinct(true); 2181db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin } 2182c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen 2183b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean hasThumbnailId = false; 2184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (table) { 2186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 2187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("images"); 2188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (uri.getQueryParameter("distinct") != null) 2189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setDistinct(true); 2190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // set the project map so that data dir is prepended to _data. 2192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project //qb.setProjectionMap(mImagesProjectionMap, true); 2193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 2196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("images"); 2197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (uri.getQueryParameter("distinct") != null) 2198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setDistinct(true); 2199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // set the project map so that data dir is prepended to _data. 2201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project //qb.setProjectionMap(mImagesProjectionMap, true); 2202baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2203baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS_ID: 2207b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen hasThumbnailId = true; 2208b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS: 2209b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) { 2210b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return null; 2211b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 2215d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 2216ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.equalsIgnoreCase("is_music=1") 2217ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen || selection.equalsIgnoreCase("is_podcast=1") ) 2218c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 2219c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 2220ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting songs"); 2221ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 2222ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 2223ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio"); 2224c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2225c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 2226c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2227c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2228c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2229c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 2230baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'"); 2231baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2232c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2233ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 2234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio"); 2238baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2239baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 2243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id IN (SELECT genre_id FROM " + 2245baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "audio_genres_map WHERE audio_id=?)"); 2246baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 2250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2251baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2252baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(5)); 2253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 2256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id IN (SELECT playlist_id FROM " + 2258baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "audio_playlists_map WHERE audio_id=?)"); 2259baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 2263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2264baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2265baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(5)); 2266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 2269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 2273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2274baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2275baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2278bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen case AUDIO_GENRES_ALL_MEMBERS: 2279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 228078b2885edc406273d688536b0eadfea006b20662Marco Nelissen { 228178b2885edc406273d688536b0eadfea006b20662Marco Nelissen // if simpleQuery is true, we can do a simpler query on just audio_genres_map 228278b2885edc406273d688536b0eadfea006b20662Marco Nelissen // we can do this if we have no keywords and our projection includes just columns 228378b2885edc406273d688536b0eadfea006b20662Marco Nelissen // from audio_genres_map 228478b2885edc406273d688536b0eadfea006b20662Marco Nelissen boolean simpleQuery = (keywords == null && projectionIn != null 228578b2885edc406273d688536b0eadfea006b20662Marco Nelissen && (selection == null || selection.equalsIgnoreCase("genre_id=?"))); 228678b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (projectionIn != null) { 228778b2885edc406273d688536b0eadfea006b20662Marco Nelissen for (int i = 0; i < projectionIn.length; i++) { 228878b2885edc406273d688536b0eadfea006b20662Marco Nelissen String p = projectionIn[i]; 228978b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (p.equals("_id")) { 229078b2885edc406273d688536b0eadfea006b20662Marco Nelissen // note, this is different from playlist below, because 229178b2885edc406273d688536b0eadfea006b20662Marco Nelissen // "_id" used to (wrongly) be the audio id in this query, not 229278b2885edc406273d688536b0eadfea006b20662Marco Nelissen // the row id of the entry in the map, and we preserve this 229378b2885edc406273d688536b0eadfea006b20662Marco Nelissen // behavior for backwards compatibility 229478b2885edc406273d688536b0eadfea006b20662Marco Nelissen simpleQuery = false; 229578b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 229678b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (simpleQuery && !(p.equals("audio_id") || 229778b2885edc406273d688536b0eadfea006b20662Marco Nelissen p.equals("genre_id"))) { 229878b2885edc406273d688536b0eadfea006b20662Marco Nelissen simpleQuery = false; 229978b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 230078b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 230178b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 230278b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (simpleQuery) { 230378b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.setTables("audio_genres_map_noid"); 2304bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen if (table == AUDIO_GENRES_ID_MEMBERS) { 2305baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("genre_id=?"); 2306baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2307bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen } 230878b2885edc406273d688536b0eadfea006b20662Marco Nelissen } else { 230978b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.setTables("audio_genres_map_noid, audio"); 2310bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen qb.appendWhere("audio._id = audio_id"); 2311bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen if (table == AUDIO_GENRES_ID_MEMBERS) { 2312baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere(" AND genre_id=?"); 2313baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2314bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen } 231578b2885edc406273d688536b0eadfea006b20662Marco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 231678b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.appendWhere(" AND "); 231778b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 231878b2885edc406273d688536b0eadfea006b20662Marco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 231978b2885edc406273d688536b0eadfea006b20662Marco Nelissen "||" + MediaStore.Audio.Media.TITLE_KEY + 2320baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2321baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 232278b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 232378b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 232478b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 2325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 2328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 2332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2333baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2334baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2337b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 2338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 2339e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // if simpleQuery is true, we can do a simpler query on just audio_playlists_map 2340e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // we can do this if we have no keywords and our projection includes just columns 2341e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // from audio_playlists_map 23424382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood boolean simpleQuery = (keywords == null && projectionIn != null 23434382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood && (selection == null || selection.equalsIgnoreCase("playlist_id=?"))); 234497e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen if (projectionIn != null) { 234597e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen for (int i = 0; i < projectionIn.length; i++) { 2346e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood String p = projectionIn[i]; 2347e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (simpleQuery && !(p.equals("audio_id") || 2348e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood p.equals("playlist_id") || p.equals("play_order"))) { 2349e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood simpleQuery = false; 2350e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } 2351e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (p.equals("_id")) { 235297e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen projectionIn[i] = "audio_playlists_map._id AS _id"; 235397e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen } 2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2356e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (simpleQuery) { 2357e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.setTables("audio_playlists_map"); 2358baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("playlist_id=?"); 2359baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2360e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } else { 2361e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.setTables("audio_playlists_map, audio"); 2362baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("audio._id = audio_id AND playlist_id=?"); 2363baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2364e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood for (int i = 0; keywords != null && i < keywords.length; i++) { 2365e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere(" AND "); 2366e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2367e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood "||" + MediaStore.Audio.Media.ALBUM_KEY + 2368e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood "||" + MediaStore.Audio.Media.TITLE_KEY + 2369baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2370baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2371e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } 2372c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2373b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) { 2374baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere(" AND audio_playlists_map._id=?"); 2375baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(5)); 2376b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen } 2377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 2380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("video"); 2381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 2383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("video"); 2384baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2385baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2388b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS_ID: 2389b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen hasThumbnailId = true; 2390b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: 2391b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) { 2392b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return null; 2393b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2394b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 2395b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS: 2397d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 2398ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.length() == 0) 2399c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 2400c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 2401ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting artists"); 2402ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 2403ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen projectionIn[0] = "count(distinct artist_id)"; 2404ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.appendWhere("is_music=1"); 2405ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 2406ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("artist_info"); 2407c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2408c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 2409c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2410c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2411c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2412baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2413baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2414c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2415ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 2416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS_ID: 2419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("artist_info"); 2420baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2421baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS_ID_ALBUMS: 2425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String aid = uri.getPathSegments().get(3); 2426acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen qb.setTables("audio LEFT OUTER JOIN album_art ON" + 2427acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen " audio.album_id=album_art.album_id"); 2428acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " + 2429baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "artists_albums_map WHERE artist_id=?)"); 2430baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(aid); 2431c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2432c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2433c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2434c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 2435baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2436baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2437c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2438acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen groupBy = "audio.album_id"; 2439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST, 2440acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " + 2441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST); 2442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setProjectionMap(sArtistAlbumsMap); 2443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMS: 2446d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 2447ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.length() == 0) 2448c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 2449c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 2450ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting albums"); 2451ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 2452ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen projectionIn[0] = "count(distinct album_id)"; 2453ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.appendWhere("is_music=1"); 2454ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 2455ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("album_info"); 2456c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2457c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 2458c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2459c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2460c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2461c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 2462baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2463baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2464c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2465ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 2466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMS_ID: 2469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("album_info"); 2470baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2471baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMART_ID: 2475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("album_art"); 2476baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("album_id=?"); 2477baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2480a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_LEGACY: 2481a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen Log.w(TAG, "Legacy media search Uri used. Please update your code."); 2482a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // fall through 2483a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_FANCY: 2484a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_BASIC: 2485baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen return doAudioSearch(db, qb, uri, projectionIn, selection, 2486baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combine(prependArgs, selectionArgs), sort, table, limit); 2487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 248816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES_ID: 2489e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 2490baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2491baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(2)); 2492b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // fall through 249316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES: 2494e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 249516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood qb.setTables("files"); 2496b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood break; 2497b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2498e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood case MTP_OBJECT_REFERENCES: 2499e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int handle = Integer.parseInt(uri.getPathSegments().get(2)); 250010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen return getObjectReferences(helper, db, handle); 2501e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 2502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 2503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalStateException("Unknown URL: " + uri.toString()); 2504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2506baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection, 2507baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen // combine(prependArgs, selectionArgs), groupBy, null, sort, limit)); 2508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = qb.query(db, projectionIn, selection, 2509baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combine(prependArgs, selectionArgs), groupBy, null, sort, limit); 2510b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (c != null) { 2512ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen String nonotify = uri.getQueryParameter("nonotify"); 2513ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen if (nonotify == null || !nonotify.equals("1")) { 2514ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen c.setNotificationUri(getContext().getContentResolver(), uri); 2515ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen } 2516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2517b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return c; 2519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2521baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen private String[] combine(List<String> prepend, String[] userArgs) { 2522baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen int presize = prepend.size(); 2523baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen if (presize == 0) { 2524baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen return userArgs; 2525baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2526baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen 2527baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen int usersize = (userArgs != null) ? userArgs.length : 0; 2528baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen String [] combined = new String[presize + usersize]; 2529baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen for (int i = 0; i < presize; i++) { 2530baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combined[i] = prepend.get(i); 2531baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2532baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen for (int i = 0; i < usersize; i++) { 2533baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combined[presize + i] = userArgs[i]; 2534baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2535baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen return combined; 2536baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2537baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen 2538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb, 2539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri, String[] projectionIn, String selection, 25404574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String[] selectionArgs, String sort, int mode, 25414574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String limit) { 2542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 254318c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment(); 2544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mSearchString = mSearchString.replaceAll(" ", " ").trim().toLowerCase(); 2545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String [] searchWords = mSearchString.length() > 0 ? 2547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mSearchString.split(" ") : new String[0]; 2548a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String [] wildcardWords = new String[searchWords.length]; 2549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int len = searchWords.length; 2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < len; i++) { 2551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Because we match on individual words here, we need to remove words 2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // like 'a' and 'the' that aren't part of the keys. 25533001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen String key = MediaStore.Audio.keyFor(searchWords[i]); 25543001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("\\", "\\\\"); 25553001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("%", "\\%"); 25563001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("_", "\\_"); 2557a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen wildcardWords[i] = 2558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project (searchWords[i].equals("a") || searchWords[i].equals("an") || 25593001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen searchWords[i].equals("the")) ? "%" : "%" + key + "%"; 2560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2562a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String where = ""; 2563a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen for (int i = 0; i < searchWords.length; i++) { 2564a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (i == 0) { 25653001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen where = "match LIKE ? ESCAPE '\\'"; 2566a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else { 25673001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen where += " AND match LIKE ? ESCAPE '\\'"; 2568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2571a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen qb.setTables("search"); 2572a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String [] cols; 2573a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (mode == AUDIO_SEARCH_FANCY) { 2574a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsFancy; 2575a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else if (mode == AUDIO_SEARCH_BASIC) { 2576a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsBasic; 2577a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else { 2578a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsLegacy; 2579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 25804574e03055af60fada50481f2b34e19a687d5866Marco Nelissen return qb.query(db, cols, where, wildcardWords, null, null, null, limit); 2581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String getType(Uri url) 2585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 2586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (URI_MATCHER.match(url)) { 2587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 2588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 2589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 2590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 2591c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood case FILES_ID: 259226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen Cursor c = null; 259326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen try { 259426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c = query(url, MIME_TYPE_PROJECTION, null, null, null); 259526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen if (c != null && c.getCount() == 1) { 259626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.moveToFirst(); 259726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen String mimeType = c.getString(1); 259826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.deactivate(); 259926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen return mimeType; 260026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } 260126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } finally { 260226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen if (c != null) { 260326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.close(); 260426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } 2605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 2609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS: 2610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Images.Media.CONTENT_TYPE; 2611804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen case AUDIO_ALBUMART_ID: 2612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS_ID: 2613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return "image/jpeg"; 2614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 2616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 2617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 2618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Media.CONTENT_TYPE; 2619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 2621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Genres.CONTENT_TYPE; 2623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 2624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 2625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Genres.ENTRY_CONTENT_TYPE; 2626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 2628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Playlists.CONTENT_TYPE; 2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Playlists.ENTRY_CONTENT_TYPE; 2632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 2634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Video.Media.CONTENT_TYPE; 2635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2636804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen throw new IllegalStateException("Unknown URL : " + url); 2637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 2640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Ensures there is a file in the _data column of values, if one isn't 264150c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen * present a new filename is generated. The file itself is not created. 2642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 2643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param initialValues the values passed to insert by the caller 2644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return the new values 2645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 2646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private ContentValues ensureFile(boolean internal, ContentValues initialValues, 2647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String preferredExtension, String directoryName) { 2648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values; 2649801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String file = initialValues.getAsString(MediaStore.MediaColumns.DATA); 2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (TextUtils.isEmpty(file)) { 2651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file = generateFileName(internal, preferredExtension, directoryName); 2652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = new ContentValues(initialValues); 2653801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood values.put(MediaStore.MediaColumns.DATA, file); 2654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = initialValues; 2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 265892013781ab1573eee3d5d75682f320fe3a6076f2Marco Nelissen // we used to create the file here, but now defer this until openFile() is called 2659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return values; 2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2662d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private void sendObjectAdded(long objectHandle) { 266334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 266434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 266534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood try { 266634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService.sendObjectAdded((int)objectHandle); 266734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } catch (RemoteException e) { 266834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Log.e(TAG, "RemoteException in sendObjectAdded", e); 266934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 267034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 2671d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2672d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2673d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2674d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 2675d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private void sendObjectRemoved(long objectHandle) { 267634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 267734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 267834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood try { 267934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService.sendObjectRemoved((int)objectHandle); 268034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } catch (RemoteException e) { 268134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Log.e(TAG, "RemoteException in sendObjectRemoved", e); 268234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 268334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 2684d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2685d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2686d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2687d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 2688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int bulkInsert(Uri uri, ContentValues values[]) { 2690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 2691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == VOLUMES) { 2692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return super.bulkInsert(uri, values); 2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 269410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 269510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null) { 2696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 2697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 2698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 269910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 27005fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 27015fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 27025fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 2703ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2704ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) { 2705ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen return playlistBulkInsert(db, uri, values); 2706e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } else if (match == MTP_OBJECT_REFERENCES) { 2707e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int handle = Integer.parseInt(uri.getPathSegments().get(2)); 270810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen return setObjectReferences(helper, db, handle, values); 2709ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2710ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 27112dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 2712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 27132dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke ArrayList<Long> notifyRowIds = new ArrayList<Long>(); 2714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int numInserted = 0; 2715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int len = values.length; 2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < len; i++) { 2718801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood if (values[i] != null) { 27192dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke insertInternal(uri, match, values[i], notifyRowIds); 2720801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood } 2721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project numInserted = len; 2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 27272dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 27282dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke // Notify MTP (outside of successful transaction) 27292dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke notifyMtp(notifyRowIds); 27302dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 2731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 2732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return numInserted; 2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2736801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood public Uri insert(Uri uri, ContentValues initialValues) { 27375d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood int match = URI_MATCHER.match(uri); 27382dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 27392dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke ArrayList<Long> notifyRowIds = new ArrayList<Long>(); 27402dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds); 27412dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke notifyMtp(notifyRowIds); 27422dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 27435d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood // do not signal notification for MTP objects. 27445d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood // we will signal instead after file transfer is successful. 2745e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood if (newUri != null && match != MTP_OBJECTS) { 2746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 2747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return newUri; 2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 27512dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke private void notifyMtp(ArrayList<Long> rowIds) { 27522dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke int size = rowIds.size(); 27532dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke for (int i = 0; i < size; i++) { 27542dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke sendObjectAdded(rowIds.get(i).longValue()); 27552dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke } 27562dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke } 27572dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 2758ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) { 2759ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen DatabaseUtils.InsertHelper helper = 2760ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen new DatabaseUtils.InsertHelper(db, "audio_playlists_map"); 27618b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID); 27628b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID); 27638b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER); 27648b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen long playlistId = Long.parseLong(uri.getPathSegments().get(3)); 2765ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2766ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.beginTransaction(); 2767ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen int numInserted = 0; 2768ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen try { 2769ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen int len = values.length; 2770ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen for (int i = 0; i < len; i++) { 27718b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.prepareForInsert(); 27728b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // getting the raw Object and converting it long ourselves saves 27738b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // an allocation (the alternative is ContentValues.getAsLong, which 27748b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // returns a Long object) 27758b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen long audioid = ((Number) values[i].get( 27768b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue(); 27778b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(audioidcolidx, audioid); 27788b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(playlistididx, playlistId); 27798b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // convert to int ourselves to save an allocation. 27808b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playorder = ((Number) values[i].get( 27818b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue(); 27828b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(playorderidx, playorder); 27838b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.execute(); 2784ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2785ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen numInserted = len; 2786ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.setTransactionSuccessful(); 2787ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } finally { 2788ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.endTransaction(); 2789ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen helper.close(); 2790ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2791ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen getContext().getContentResolver().notifyChange(uri, null); 2792ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen return numInserted; 2793ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2794ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 279510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) { 279610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path); 2797ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood ContentValues values = new ContentValues(); 2798ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION); 2799ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood values.put(FileColumns.DATA, path); 280010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values.put(FileColumns.PARENT, getParent(helper, db, path)); 28019be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood values.put(FileColumns.STORAGE_ID, getStorageId(path)); 2802f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood File file = new File(path); 2803f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (file.exists()) { 2804f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000); 2805f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood } 280610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 2807ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values); 2808ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood sendObjectAdded(rowId); 2809ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood return rowId; 2810ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 2811ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 281210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) { 2813b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood int lastSlash = path.lastIndexOf('/'); 2814b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (lastSlash > 0) { 2815b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood String parentPath = path.substring(0, lastSlash); 28169be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood for (int i = 0; i < mExternalStoragePaths.length; i++) { 28179be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (parentPath.equals(mExternalStoragePaths[i])) { 28189be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return 0; 28199be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 2820b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 28217f36494e085c26c69cd5925e54028822025eff29Marco Nelissen Long cid = mDirectoryCache.get(parentPath); 28227f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (cid != null) { 28237f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath); 28247f36494e085c26c69cd5925e54028822025eff29Marco Nelissen return cid; 28257f36494e085c26c69cd5925e54028822025eff29Marco Nelissen } 28267f36494e085c26c69cd5925e54028822025eff29Marco Nelissen 2827e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen String selection = MediaStore.MediaColumns.DATA + "=?"; 2828b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood String [] selargs = { parentPath }; 282910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 28307f36494e085c26c69cd5925e54028822025eff29Marco Nelissen Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null); 2831b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood try { 28327f36494e085c26c69cd5925e54028822025eff29Marco Nelissen long id; 2833b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (c == null || c.getCount() == 0) { 2834b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // parent isn't in the database - so add it 283510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen id = insertDirectory(helper, db, parentPath); 28367f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath); 2837b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } else { 28385461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen if (c.getCount() > 1) { 28395461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen Log.e(TAG, "more than one match for " + parentPath); 28405461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } 2841b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood c.moveToFirst(); 28427f36494e085c26c69cd5925e54028822025eff29Marco Nelissen id = c.getLong(0); 28437f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath); 2844b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 28457f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.put(parentPath, id); 28467f36494e085c26c69cd5925e54028822025eff29Marco Nelissen return id; 2847b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } finally { 2848b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (c != null) c.close(); 2849b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2850b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } else { 2851b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood return 0; 2852b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2853b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2854b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 28559be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood private int getStorageId(String path) { 28569be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood for (int i = 0; i < mExternalStoragePaths.length; i++) { 28579be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood String test = mExternalStoragePaths[i]; 28589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (path.startsWith(test)) { 28599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood int length = test.length(); 28609be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (path.length() == length || path.charAt(length) == '/') { 28619be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return MtpStorage.getStorageId(i); 28629be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 28639be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 28649be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 28659be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood // default to primary storage 28669be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return MtpStorage.getStorageId(0); 28679be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 28689be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood 286910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType, 28702dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke boolean notify, ArrayList<Long> notifyRowIds) { 287110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 2872afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood ContentValues values = null; 2873afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2874afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood switch (mediaType) { 2875afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_IMAGE: { 287610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values = ensureFile(helper.mInternal, initialValues, ".jpg", "DCIM/Camera"); 2877afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2878afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000); 2879afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String data = values.getAsString(MediaColumns.DATA); 2880afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (! values.containsKey(MediaColumns.DISPLAY_NAME)) { 2881afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeDisplayName(data, values); 2882afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2883afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeTakenTime(values); 2884afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 2885afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2886afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2887afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_AUDIO: { 2888afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // SQLite Views are read-only, so we need to deconstruct this 2889afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // insert and do inserts into the underlying tables. 2890afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // If doing this here turns out to be a performance bottleneck, 2891afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // consider moving this to native code and using triggers on 2892afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // the view. 2893afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(initialValues); 2894afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 28952658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST); 28962658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION); 28972658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen values.remove(MediaStore.Audio.Media.COMPILATION); 28982658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 2899afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Insert the artist into the artist table and remove it from 2900afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // the input values 2901afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Object so = values.get("artist"); 2902afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String s = (so == null ? "" : so.toString()); 2903afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("artist"); 2904afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long artistRowId; 290510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> artistCache = helper.mArtistCache; 2906801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String path = values.getAsString(MediaStore.MediaColumns.DATA); 2907afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood synchronized(artistCache) { 2908afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long temp = artistCache.get(s); 2909afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (temp == null) { 291010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen artistRowId = getKeyIdForName(helper, db, 291110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "artists", "artist_key", "artist", 2912afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s, s, path, 0, null, artistCache, uri); 2913afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 2914afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood artistRowId = temp.longValue(); 2915afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2916afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2917afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String artist = s; 2918afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2919afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Do the same for the album field 2920afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood so = values.get("album"); 2921afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s = (so == null ? "" : so.toString()); 2922afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("album"); 2923afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long albumRowId; 292410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> albumCache = helper.mAlbumCache; 2925afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood synchronized(albumCache) { 29262658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen int albumhash = 0; 29272658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (albumartist != null) { 29282658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumhash = albumartist.hashCode(); 29292658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else if (compilation != null && compilation.equals("1")) { 29302658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // nothing to do, hash already set 29312658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else { 29322658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumhash = path.substring(0, path.lastIndexOf('/')).hashCode(); 29332658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } 2934afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String cacheName = s + albumhash; 2935afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long temp = albumCache.get(cacheName); 2936afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (temp == null) { 293710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen albumRowId = getKeyIdForName(helper, db, 293810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "albums", "album_key", "album", 2939afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s, cacheName, path, albumhash, artist, albumCache, uri); 2940afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 2941afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood albumRowId = temp; 2942afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2943afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 29445d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood 2945afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("artist_id", Integer.toString((int)artistRowId)); 2946afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("album_id", Integer.toString((int)albumRowId)); 2947afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood so = values.getAsString("title"); 2948afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s = (so == null ? "" : so.toString()); 2949afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("title_key", MediaStore.Audio.keyFor(s)); 2950afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // do a final trim of the title, in case it started with the special 2951afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // "sort first" character (ascii \001) 2952afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("title"); 2953afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("title", s.trim()); 2954b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2955801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values); 2956afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 2957afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2958afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2959afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_VIDEO: { 296010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values = ensureFile(helper.mInternal, initialValues, ".3gp", "video"); 2961801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String data = values.getAsString(MediaStore.MediaColumns.DATA); 2962afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeDisplayName(data, values); 2963afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeTakenTime(values); 2964afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 2965afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2966afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2967afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2968afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (values == null) { 2969afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(initialValues); 2970afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2971fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // compute bucket_id and bucket_display_name for all files 2972afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String path = values.getAsString(MediaStore.MediaColumns.DATA); 2973fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood if (path != null) { 2974fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood computeBucketValues(path, values); 2975fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood } 2976fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000); 2977afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2978afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long rowId = 0; 2979afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Integer i = values.getAsInteger( 2980afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID); 2981afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (i != null) { 2982afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = i.intValue(); 2983afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(values); 2984afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID); 2985afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2986afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2987afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String title = values.getAsString(MediaStore.MediaColumns.TITLE); 29883572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen if (title == null && path != null) { 2989c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood title = MediaFile.getFileTitle(path); 2990c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2991c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.TITLE, title); 2992c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 2993afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE); 2994afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Integer formatObject = values.getAsInteger(FileColumns.FORMAT); 2995c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood int format = (formatObject == null ? 0 : formatObject.intValue()); 2996c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (format == 0) { 299763b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang if (TextUtils.isEmpty(path)) { 2998c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood // special case device created playlists 2999afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) { 3000c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST); 3001c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood // create a file path for the benefit of MTP 30029be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood path = mExternalStoragePaths[0] 3003afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "/Playlists/" + values.getAsString(Audio.Playlists.NAME); 3004c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(MediaStore.MediaColumns.DATA, path); 300510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values.put(FileColumns.PARENT, getParent(helper, db, path)); 3006c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } else { 300763b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang Log.e(TAG, "path is empty in insertFile()"); 3008c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3009c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } else { 3010c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood format = MediaFile.getFormatCode(path, mimeType); 3011c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3012c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3013c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (format != 0) { 3014c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.FORMAT, format); 3015c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (mimeType == null) { 3016c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood mimeType = MediaFile.getMimeTypeForFormatCode(format); 3017c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3018c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3019c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 3020801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood if (mimeType == null && path != null) { 3021c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood mimeType = MediaFile.getMimeTypeForFile(path); 3022c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3023c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (mimeType != null) { 3024c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.MIME_TYPE, mimeType); 3025afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3026f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) { 3027afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int fileType = MediaFile.getFileTypeForMimeType(mimeType); 3028afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (MediaFile.isAudioFileType(fileType)) { 3029afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_AUDIO; 3030afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isVideoFileType(fileType)) { 3031afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_VIDEO; 3032afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isImageFileType(fileType)) { 3033afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_IMAGE; 3034afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isPlayListFileType(fileType)) { 3035afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_PLAYLIST; 3036afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3037afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3038c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3039afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put(FileColumns.MEDIA_TYPE, mediaType); 3040c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 3041afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (rowId == 0) { 3042afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) { 30433572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen String name = values.getAsString(Audio.Playlists.NAME); 3044282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood if (name == null && path == null) { 3045282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood // MediaScanner will compute the name from the path if we have one 3046afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood throw new IllegalArgumentException( 3047282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood "no name was provided when inserting abstract playlist"); 3048afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3049afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 3050a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu if (path == null) { 3051afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // path might be null for playlists created on the device 3052afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // or transfered via MTP 3053afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood throw new IllegalArgumentException( 3054afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "no path was provided when inserting new file"); 3055afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3056e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3057b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 3058f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood // make sure modification date and size are set 3059f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (path != null) { 3060f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood File file = new File(path); 3061f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (file.exists()) { 3062f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000); 306334d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen if (!values.containsKey(FileColumns.SIZE)) { 306434d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen values.put(FileColumns.SIZE, file.length()); 306534d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen } 306620368a558593c685623bfff6df1f4545d53f8291Jia Su // make sure date taken time is set 306720368a558593c685623bfff6df1f4545d53f8291Jia Su if (mediaType == FileColumns.MEDIA_TYPE_IMAGE 306820368a558593c685623bfff6df1f4545d53f8291Jia Su || mediaType == FileColumns.MEDIA_TYPE_VIDEO) { 306920368a558593c685623bfff6df1f4545d53f8291Jia Su computeTakenTime(values); 307020368a558593c685623bfff6df1f4545d53f8291Jia Su } 3071e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 30725d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 3073b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 3074afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long parent = values.getAsLong(FileColumns.PARENT); 30755d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood if (parent == null) { 3076e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (path != null) { 307710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long parentId = getParent(helper, db, path); 307816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood values.put(FileColumns.PARENT, parentId); 3079e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 30809be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 30819be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood Integer storage = values.getAsInteger(FileColumns.STORAGE_ID); 30829be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (storage == null) { 30839be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood int storageId = getStorageId(path); 30849be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood values.put(FileColumns.STORAGE_ID, storageId); 30855d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 3086b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 308710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3088afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = db.insert("files", FileColumns.DATE_MODIFIED, values); 3089afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId); 3090afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 309110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (rowId != -1 && notify) { 30922dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke notifyRowIds.add(rowId); 3093afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 30945d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } else { 309510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 309616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.update("files", values, FileColumns._ID + "=?", 3097afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood new String[] { Long.toString(rowId) }); 30985d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 3099d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen if (format == MtpConstants.FORMAT_ASSOCIATION) { 3100d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen mDirectoryCache.put(path, rowId); 3101d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen } 3102afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3103afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood return rowId; 31041717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood } 31051717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 310610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) { 310710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3108a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen Cursor c = db.query("files", sMediaTableColumns, "_id=?", 3109e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Integer.toString(handle) }, 3110e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 3111e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 3112e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 3113afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long playlistId = c.getLong(0); 3114afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 3115afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) { 3116e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only support object references for playlist objects 3117e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return null; 3118e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 311910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3120e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return db.rawQuery(OBJECT_REFERENCES_QUERY, 3121afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood new String[] { Long.toString(playlistId) } ); 3122e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3123e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 3124e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 3125e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 3126e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3127e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3128e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return null; 3129e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3130e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 313110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db, 313210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int handle, ContentValues values[]) { 3133e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // first look up the media table and media ID for the object 3134e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long playlistId = 0; 313510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3136a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen Cursor c = db.query("files", sMediaTableColumns, "_id=?", 3137e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Integer.toString(handle) }, 3138e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 3139e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 3140e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 3141afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 3142afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) { 3143e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only support object references for playlist objects 3144e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return 0; 3145e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3146afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood playlistId = c.getLong(0); 3147e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3148e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 3149e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 3150e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 3151e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3152e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3153e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (playlistId == 0) { 3154e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return 0; 3155e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3156e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 3157e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // next delete any existing entries 315810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumDeletes++; 315910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen db.delete("audio_playlists_map", "playlist_id=?", 3160e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Long.toString(playlistId) }); 3161e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 3162e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // finally add the new entries 3163e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int count = values.length; 3164e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int added = 0; 3165e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues[] valuesList = new ContentValues[count]; 3166e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood for (int i = 0; i < count; i++) { 3167e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // convert object ID to audio ID 3168e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long audioId = 0; 3169e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID); 317010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3171a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen c = db.query("files", sMediaTableColumns, "_id=?", 3172e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Long.toString(objectId) }, 3173e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 3174e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 3175e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 3176afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 317750d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) { 3178e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only allow audio files in playlists, so skip 3179e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood continue; 3180e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3181afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood audioId = c.getLong(0); 3182e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3183e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 3184e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 3185e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 3186e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3187e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3188e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (audioId != 0) { 3189e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues v = new ContentValues(); 3190e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId); 3191e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId); 3192a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added); 3193a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood valuesList[added++] = v; 3194e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3195e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3196e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (added < count) { 3197e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we weren't able to find everything on the list, so lets resize the array 3198e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // and pass what we have. 3199e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues[] newValues = new ContentValues[added]; 3200e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood System.arraycopy(valuesList, 0, newValues, 0, added); 3201e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood valuesList = newValues; 3202e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3203e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return playlistBulkInsert(db, 3204e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId), 3205e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood valuesList); 3206e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3207e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 3208b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood private static final String[] GENRE_LOOKUP_PROJECTION = new String[] { 3209b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Audio.Genres._ID, // 0 3210b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Audio.Genres.NAME, // 1 3211b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood }; 3212b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 3213b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood private void updateGenre(long rowId, String genre) { 3214b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Uri uri = null; 3215b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Cursor cursor = null; 3216b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Uri genresUri = MediaStore.Audio.Genres.getContentUri("external"); 3217b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood try { 3218b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // see if the genre already exists 3219b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?", 3220b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood new String[] { genre }, null); 3221b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (cursor == null || cursor.getCount() == 0) { 3222b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // genre does not exist, so create the genre in the genre table 3223b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood ContentValues values = new ContentValues(); 3224b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood values.put(MediaStore.Audio.Genres.NAME, genre); 3225b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood uri = insert(genresUri, values); 3226b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } else { 3227b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // genre already exists, so compute its Uri 3228b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood cursor.moveToNext(); 3229b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0)); 3230b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3231b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (uri != null) { 3232b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY); 3233b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3234b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } finally { 3235b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // release the cursor if it exists 3236b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (cursor != null) { 3237b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood cursor.close(); 3238b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3239b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3240b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 3241b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (uri != null) { 3242b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // add entry to audio_genre_map 3243b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood ContentValues values = new ContentValues(); 3244b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId)); 3245b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood insert(uri, values); 3246b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3247b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3248b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 32492dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke private Uri insertInternal(Uri uri, int match, ContentValues initialValues, 32502dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke ArrayList<Long> notifyRowIds) { 3251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long rowId; 3252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3253d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues); 3254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 3255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == MEDIA_SCANNER) { 3256bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME); 325710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper database = getDatabaseForUri( 325810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Uri.parse("content://media/" + mMediaScannerVolume + "/audio")); 325910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (database == null) { 326010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume); 326110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } else { 326210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mScanStartTime = SystemClock.currentTimeMicro(); 326310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 3264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return MediaStore.getMediaScannerUri(); 3265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3267b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood String genre = null; 326838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen String path = null; 3269b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (initialValues != null) { 3270b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood genre = initialValues.getAsString(Audio.AudioColumns.GENRE); 3271b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood initialValues.remove(Audio.AudioColumns.GENRE); 327238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen path = initialValues.getAsString(MediaStore.MediaColumns.DATA); 3273b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3274b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 327538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 3276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri newUri = null; 327710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 327810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null && match != VOLUMES && match != MTP_CONNECTED) { 3279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 3281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 328210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen 3283819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null 328410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen : helper.getWritableDatabase()); 3285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 3287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: { 32882dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, initialValues, 32892dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds); 3290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId( 3292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId); 3293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3297b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // This will be triggered by requestMediaThumbnail (see getThumbnailUri) 3298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS: { 329910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg", 3300b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "DCIM/.thumbnails"); 330110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("thumbnails", "name", values); 3303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Images.Thumbnails. 3305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContentUri(uri.getPathSegments().get(0)), rowId); 3306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3310b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri) 3311b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: { 331210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg", 3313b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "DCIM/.thumbnails"); 331410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3315b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen rowId = db.insert("videothumbnails", "name", values); 3316b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (rowId > 0) { 3317b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen newUri = ContentUris.withAppendedId(Video.Thumbnails. 3318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen getContentUri(uri.getPathSegments().get(0)), rowId); 3319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3320b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 3321b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3322b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 3323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: { 33242dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, initialValues, 33252dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds); 3326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId); 3328b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (genre != null) { 3329b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood updateGenre(rowId, genre); 3330b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: { 3336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long audioId = Long.parseLong(uri.getPathSegments().get(2)); 3337bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Genres.Members.AUDIO_ID, audioId); 333910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3340ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen rowId = db.insert("audio_genres_map", "genre_id", values); 3341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: { 3348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long audioId = Long.parseLong(uri.getPathSegments().get(2)); 3349bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Playlists.Members.AUDIO_ID, audioId); 335110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("audio_playlists_map", "playlist_id", 3353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values); 3354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: { 336110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3362bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood rowId = db.insert("audio_genres", "audio_id", initialValues); 3363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId); 3365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: { 3370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long genreId = Long.parseLong(uri.getPathSegments().get(3)); 3371bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Genres.Members.GENRE_ID, genreId); 337310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("audio_genres_map", "genre_id", values); 3375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: { 3382bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000); 33842dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, values, 33852dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds); 3386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId); 3388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 3393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: { 3394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long playlistId = Long.parseLong(uri.getPathSegments().get(3)); 3395bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId); 339710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3398ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen rowId = db.insert("audio_playlists_map", "playlist_id", values); 3399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: { 34062dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, initialValues, 34072dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds); 3408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3409b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen newUri = ContentUris.withAppendedId(Video.Media.getContentUri( 3410b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen uri.getPathSegments().get(0)), rowId); 3411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3415c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood case AUDIO_ALBUMART: { 341610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper.mInternal) { 3417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("no internal album art allowed"); 3418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3419bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = null; 3420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 3421bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER); 3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (IllegalStateException ex) { 3423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // probably no more room to store albumthumbs 3424bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood values = initialValues; 3425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 342610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3427801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values); 3428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3432c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VOLUMES: 34355619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen { 34365619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen String name = initialValues.getAsString("name"); 34375619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen Uri attachedVolume = attachVolume(name); 34385619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) { 34395619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen DatabaseHelper dbhelper = getDatabaseForUri(attachedVolume); 34405619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen if (dbhelper == null) { 34415619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen Log.e(TAG, "no database for attached volume " + attachedVolume); 34425619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } else { 34435619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen dbhelper.mScanStartTime = SystemClock.currentTimeMicro(); 34445619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } 34455619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } 34465619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen return attachedVolume; 34475619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } 3448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3449819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood case MTP_CONNECTED: 345034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 345134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService == null) { 345234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Context context = getContext(); 345334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // MTP is connected, so grab a connection to MtpService 345434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood context.bindService(new Intent(context, MtpService.class), 345534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpServiceConnection, Context.BIND_AUTO_CREATE); 345634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 345734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 3458819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood break; 3459819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood 3460afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FILES: 346110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen rowId = insertFile(helper, uri, initialValues, 34622dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds); 3463fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood if (rowId > 0) { 3464fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId); 3465fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood } 3466fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood break; 3467fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood 3468e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 34692dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke // We don't send a notification if the insert originated from MTP 347010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen rowId = insertFile(helper, uri, initialValues, 34712dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds); 3472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (rowId > 0) { 3473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId); 34745d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 34755d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood break; 34765d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood 3477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("Invalid URI " + uri); 3479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 348138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) { 348238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen // need to set the media_type of all the files below this folder to 0 348338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processNewNoMediaPath(helper, db, path); 348438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return newUri; 3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 348838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen /* 348938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * Sets the media type of all files below the newly added .nomedia file or 349038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * hidden folder to 0, so the entries no longer appear in e.g. the audio and 349138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * images views. 349238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * 349338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * @param path The path to the new .nomedia file or hidden directory 349438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen */ 34955461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen private void processNewNoMediaPath(final DatabaseHelper helper, final SQLiteDatabase db, 34965461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen final String path) { 34975461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen final File nomedia = new File(path); 349838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (nomedia.exists()) { 34995461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen hidePath(helper, db, path); 35005461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } else { 35015461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen // File doesn't exist. Try again in a little while. 35025461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen // XXX there's probably a better way of doing this 35035461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen new Thread(new Runnable() { 35045461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen @Override 35055461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen public void run() { 35065461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen SystemClock.sleep(2000); 35075461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen if (nomedia.exists()) { 35085461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen hidePath(helper, db, path); 35095461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } else { 35105461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen Log.w(TAG, "does not exist: " + path, new Exception()); 35115461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } 35125461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen }}).start(); 351338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 351438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 351538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 35165461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen private void hidePath(DatabaseHelper helper, SQLiteDatabase db, String path) { 35175461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen File nomedia = new File(path); 35185461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent(); 35195461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen ContentValues mediatype = new ContentValues(); 35205461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen mediatype.put("media_type", 0); 35215461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen int numrows = db.update("files", mediatype, 3522a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync "_data >= ? AND _data < ?", 3523a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang new String[] { hiddenroot + "/", hiddenroot + "0"}); 35245461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen helper.mNumUpdates += numrows; 35255461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen ContentResolver res = getContext().getContentResolver(); 35265461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen res.notifyChange(Uri.parse("content://media/"), null); 35275461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } 35285461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen 352938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen /* 353038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * Rescan files for missing metadata and set their type accordingly. 353138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * There is code for detecting the removal of a nomedia file or renaming of 353238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * a directory from hidden to non-hidden in the MediaScanner and MtpDatabase, 353338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * both of which call here. 353438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen */ 353538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen private void processRemovedNoMediaPath(final String path) { 353638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen final DatabaseHelper helper; 353738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (path.startsWith(mExternalStoragePaths[0])) { 353838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen helper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI); 353938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } else { 354038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen helper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI); 354138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 354238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 354338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen new ScannerClient(getContext(), db, path); 354438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 354538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 354638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen private static final class ScannerClient implements MediaScannerConnectionClient { 354738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen String mPath = null; 354838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen MediaScannerConnection mScannerConnection; 354938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen SQLiteDatabase mDb; 355038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 355138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public ScannerClient(Context context, SQLiteDatabase db, String path) { 355238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mDb = db; 355338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mPath = path; 355438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection = new MediaScannerConnection(context, this); 355538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection.connect(); 355638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 355738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 355838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen @Override 355938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public void onMediaScannerConnected() { 35605461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen Cursor c = mDb.query("files", openFileColumns, 3561a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync "_data >= ? AND _data < ?", 3562a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang new String[] { mPath + "/", mPath + "0"}, 35635461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen null, null, null); 356438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen while (c.moveToNext()) { 356538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen String d = c.getString(0); 356638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen File f = new File(d); 356738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (f.isFile()) { 356838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection.scanFile(d, null); 356938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 357038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 357138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection.disconnect(); 357238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen c.close(); 357338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 357438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 357538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen @Override 357638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public void onScanCompleted(String path, Uri uri) { 357738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 357838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 357938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 3580cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen @Override 3581cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 3582cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen throws OperationApplicationException { 3583cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 3584cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // The operations array provides no overall information about the URI(s) being operated 3585cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // on, so begin a transaction for ALL of the databases. 3586cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI); 3587cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI); 3588cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen SQLiteDatabase idb = ihelper.getWritableDatabase(); 3589cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.beginTransaction(); 3590cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen SQLiteDatabase edb = null; 3591cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (ehelper != null) { 3592cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb = ehelper.getWritableDatabase(); 3593cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.beginTransaction(); 3594cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3595cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen try { 3596cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen ContentProviderResult[] result = super.applyBatch(operations); 3597cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.setTransactionSuccessful(); 3598cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (edb != null) { 3599cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.setTransactionSuccessful(); 3600cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3601cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // Rather than sending targeted change notifications for every Uri 3602cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // affected by the batch operation, just invalidate the entire internal 3603cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // and external name space. 3604cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen ContentResolver res = getContext().getContentResolver(); 3605cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen res.notifyChange(Uri.parse("content://media/"), null); 3606cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen return result; 3607cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } finally { 3608cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.endTransaction(); 3609cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (edb != null) { 3610cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.endTransaction(); 3611cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3612cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3613cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3614cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 3615cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 36169299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) { 3617b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mMediaThumbQueue) { 3618e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen MediaThumbRequest req = null; 3619e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen try { 3620e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen req = new MediaThumbRequest( 36219299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen getContext().getContentResolver(), path, uri, priority, magic); 3622e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen mMediaThumbQueue.add(req); 3623e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen // Trigger the handler. 3624e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB); 3625e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen msg.sendToTarget(); 3626e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } catch (Throwable t) { 3627e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Log.w(TAG, t); 3628e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } 3629b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return req; 3630b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3632b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 3633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String generateFileName(boolean internal, String preferredExtension, String directoryName) 3634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 3635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create a random file 3636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name = String.valueOf(System.currentTimeMillis()); 3637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (internal) { 3639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("Writing to internal storage is not supported."); 3640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project// return Environment.getDataDirectory() 3641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project// + "/" + directoryName + "/" + name + preferredExtension; 3642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 36439be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension; 3644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private boolean ensureFileExists(String path) { 3648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(path); 3649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (file.exists()) { 3650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return true; 3651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we will not attempt to create the first directory in the path 3653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // (for example, do not create /sdcard if the SD card is not mounted) 3654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int secondSlash = path.indexOf('/', 1); 3655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (secondSlash < 1) return false; 3656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String directoryPath = path.substring(0, secondSlash); 3657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File directory = new File(directoryPath); 3658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!directory.exists()) 3659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return false; 366017ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood file.getParentFile().mkdirs(); 3661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 366217ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood return file.createNewFile(); 3663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch(IOException ioe) { 3664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "File creation failed", ioe); 3665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return false; 3667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final class GetTableAndWhereOutParameter { 3671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String table; 3672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String where; 3673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final GetTableAndWhereOutParameter sGetTableAndWhereParam = 3676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new GetTableAndWhereOutParameter(); 3677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private void getTableAndWhere(Uri uri, int match, String userWhere, 3679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project GetTableAndWhereOutParameter out) { 3680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String where = null; 3681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 36829f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin case IMAGES_MEDIA: 3683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE; 36859f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin break; 36869f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin 3687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 3688afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id = " + uri.getPathSegments().get(3); 3690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3692b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS_ID: 3693b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen where = "_id=" + uri.getPathSegments().get(3); 3694b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS: 3695b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen out.table = "thumbnails"; 3696b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 3697b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 3698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 3699afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO; 3701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 3704afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 3709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3); 3711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 3714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3) + 3716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND genre_id=" + uri.getPathSegments().get(5); 3717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 3720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists"; 3721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3); 3722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 3725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists"; 3726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3) + 3727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND playlists_id=" + uri.getPathSegments().get(5); 3728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 3731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 3735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 3740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "genre_id=" + uri.getPathSegments().get(3); 3742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 3745afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3746afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST; 3747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 3750afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 3755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists_map"; 3756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "playlist_id=" + uri.getPathSegments().get(3); 3757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 3760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists_map"; 3761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "playlist_id=" + uri.getPathSegments().get(3) + 3762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND _id=" + uri.getPathSegments().get(5); 3763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMART_ID: 3766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "album_art"; 3767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "album_id=" + uri.getPathSegments().get(3); 3768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 3771afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3772afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO; 3773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 3776afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3780b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS_ID: 3781b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen where = "_id=" + uri.getPathSegments().get(3); 3782b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: 3783b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen out.table = "videothumbnails"; 3784b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 3785b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 378616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES_ID: 3787e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 37881717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood where = "_id=" + uri.getPathSegments().get(2); 378916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES: 3790e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 379116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood out.table = "files"; 37921717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood break; 37931717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 3794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown or unsupported URL: " + uri.toString()); 3797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Add in the user requested WHERE clause, if needed 3800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!TextUtils.isEmpty(userWhere)) { 3801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!TextUtils.isEmpty(where)) { 3802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = where + " AND (" + userWhere + ")"; 3803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = userWhere; 3805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = where; 3808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 3812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int delete(Uri uri, String userWhere, String[] whereArgs) { 3813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count; 3814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 3815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 3817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == MEDIA_SCANNER) { 3818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mMediaScannerVolume == null) { 3819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return 0; 3820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 382110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper database = getDatabaseForUri( 382210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Uri.parse("content://media/" + mMediaScannerVolume + "/audio")); 382310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (database == null) { 382410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume); 382510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } else { 382610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mScanStopTime = SystemClock.currentTimeMicro(); 3827988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen String msg = dump(database, false); 3828988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen logToDb(database.getWritableDatabase(), msg); 382910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 3830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mMediaScannerVolume = null; 3831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return 1; 3832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3834819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood if (match == VOLUMES_ID) { 3835819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood detachVolume(uri); 3836819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood count = 1; 3837819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood } else if (match == MTP_CONNECTED) { 383834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 383934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 384034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // MTP has disconnected, so release our connection to MtpService 384134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood getContext().unbindService(mMtpServiceConnection); 384234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood count = 1; 384334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // mMtpServiceConnection.onServiceDisconnected might not get called, 384434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // so set mMtpService = null here 384534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 384634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } else { 384734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood count = 0; 384834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 384934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 3850819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood } else { 3851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 3852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 3853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3854819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood "Unknown URI: " + uri + " match: " + match); 3855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 385610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 3857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getWritableDatabase(); 3858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (sGetTableAndWhereParam) { 3860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam); 3861a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen 3862a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen if (sGetTableAndWhereParam.table.equals("files")) { 3863d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA); 3864d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen if (deleteparam == null || ! deleteparam.equals("false")) { 3865d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen database.mNumQueries++; 3866d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen Cursor c = db.query(sGetTableAndWhereParam.table, 3867d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen sMediaTypeDataId, 3868d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen sGetTableAndWhereParam.where, whereArgs, null, null, null); 3869d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen String [] idvalue = new String[] { "" }; 38704eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen String [] playlistvalues = new String[] { "", "" }; 3871d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen while (c.moveToNext()) { 3872d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen int mediatype = c.getInt(0); 3873d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen if (mediatype == FileColumns.MEDIA_TYPE_IMAGE) { 3874d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen try { 3875d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen Libcore.os.remove(c.getString(1)); 3876d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen idvalue[0] = "" + c.getLong(2); 3877d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen database.mNumQueries++; 3878d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen Cursor cc = db.query("thumbnails", sDataOnlyColumn, 3879d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen "image_id=?", idvalue, null, null, null); 3880d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen while (cc.moveToNext()) { 3881d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen Libcore.os.remove(cc.getString(0)); 3882d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen } 3883d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen cc.close(); 3884d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen database.mNumDeletes++; 3885d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen db.delete("thumbnails", "image_id=?", idvalue); 3886d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen } catch (ErrnoException e) { 3887a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 3888d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen } else if (mediatype == FileColumns.MEDIA_TYPE_VIDEO) { 3889d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen try { 3890d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen Libcore.os.remove(c.getString(1)); 3891d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen } catch (ErrnoException e) { 3892d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen } 3893d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen } else if (mediatype == FileColumns.MEDIA_TYPE_AUDIO) { 3894f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen if (!database.mInternal) { 3895f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen idvalue[0] = "" + c.getLong(2); 38969b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumDeletes += 2; // also count the one below 3897f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen db.delete("audio_genres_map", "audio_id=?", idvalue); 38984eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen // for each playlist that the item appears in, move 38994eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen // all the items behind it forward by one 39004eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Cursor cc = db.query("audio_playlists_map", 39014eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen sPlaylistIdPlayOrder, 39024eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen "audio_id=?", idvalue, null, null, null); 39034eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen while (cc.moveToNext()) { 39044eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen playlistvalues[0] = "" + cc.getLong(0); 39054eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen playlistvalues[1] = "" + cc.getInt(1); 39069b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumUpdates++; 39074eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen db.execSQL("UPDATE audio_playlists_map" + 39084eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " SET play_order=play_order-1" + 39094eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE playlist_id=? AND play_order>?", 39104eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen playlistvalues); 39114eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen } 39124eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen cc.close(); 3913f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen db.delete("audio_playlists_map", "audio_id=?", idvalue); 3914f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen } 3915d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen } else if (mediatype == FileColumns.MEDIA_TYPE_PLAYLIST) { 3916d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen // TODO, maybe: remove the audio_playlists_cleanup trigger and implement 3917d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen // it functionality here (clean up the playlist map) 39185afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen } 3919a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 3920d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen c.close(); 3921a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 3922a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 3923a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen 3924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 392536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood case MTP_OBJECTS: 3926e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 3927d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood try { 3928d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // don't send objectRemoved event since this originated from MTP 3929d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood mDisableMtpObjectCallbacks = true; 393010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 393110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen count = db.delete("files", sGetTableAndWhereParam.where, whereArgs); 3932d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } finally { 3933d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood mDisableMtpObjectCallbacks = false; 3934d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 393536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood break; 393678b2885edc406273d688536b0eadfea006b20662Marco Nelissen case AUDIO_GENRES_ID_MEMBERS: 393710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 393878b2885edc406273d688536b0eadfea006b20662Marco Nelissen count = db.delete("audio_genres_map", 393978b2885edc406273d688536b0eadfea006b20662Marco Nelissen sGetTableAndWhereParam.where, whereArgs); 394078b2885edc406273d688536b0eadfea006b20662Marco Nelissen break; 3941166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen 3942a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen case IMAGES_THUMBNAILS_ID: 3943a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen case IMAGES_THUMBNAILS: 3944166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen case VIDEO_THUMBNAILS_ID: 3945166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen case VIDEO_THUMBNAILS: 3946166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen // Delete the referenced files first. 3947166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen Cursor c = db.query(sGetTableAndWhereParam.table, 3948166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen sDataOnlyColumn, 3949166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen sGetTableAndWhereParam.where, whereArgs, null, null, null); 3950166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen if (c != null) { 3951166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen while (c.moveToNext()) { 3952166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen try { 3953166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen Libcore.os.remove(c.getString(0)); 3954166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } catch (ErrnoException e) { 3955166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } 3956166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } 3957166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen c.close(); 3958166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } 3959166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen database.mNumDeletes++; 3960166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen count = db.delete(sGetTableAndWhereParam.table, 3961166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen sGetTableAndWhereParam.where, whereArgs); 3962166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen break; 3963166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen 3964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 396510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 3966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.delete(sGetTableAndWhereParam.table, 3967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 3968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 39703631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // Since there are multiple Uris that can refer to the same files 39713631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // and deletes can affect other objects in storage (like subdirectories 39723631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // or playlists) we will notify a change on the entire volume to make 39733631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // sure no listeners miss the notification. 39743631d46b679a64a16918698121916b60d7c86e97Mike Lockwood String volume = uri.getPathSegments().get(0); 39753631d46b679a64a16918698121916b60d7c86e97Mike Lockwood Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume); 39763631d46b679a64a16918698121916b60d7c86e97Mike Lockwood getContext().getContentResolver().notifyChange(notifyUri, null); 3977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return count; 3981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 398438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public Bundle call(String method, String arg, Bundle extras) { 398538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (MediaStore.UNHIDE_CALL.equals(method)) { 398638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processRemovedNoMediaPath(arg); 398738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen return null; 398838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 398938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen throw new UnsupportedOperationException("Unsupported call: " + method); 399038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 399138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 399238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen @Override 3993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int update(Uri uri, ContentValues initialValues, String userWhere, 3994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] whereArgs) { 3995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count; 3996b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues); 3997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 399810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 399910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null) { 4000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 4001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 4002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 400310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 400410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen 400510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 4006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4007b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood String genre = null; 4008b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (initialValues != null) { 4009b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood genre = initialValues.getAsString(Audio.AudioColumns.GENRE); 4010b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood initialValues.remove(Audio.AudioColumns.GENRE); 4011b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 4012b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 4013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (sGetTableAndWhereParam) { 4014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam); 4015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 40161d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // special case renaming directories via MTP. 40171d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // in this case we must update all paths in the database with 40181d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // the directory name as a prefix 40191d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID) 40201d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood && initialValues != null && initialValues.size() == 1) { 40211d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood String oldPath = null; 4022801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA); 40237f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.remove(newPath); 40241d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // MtpDatabase will rename the directory first, so we test the new file name 402538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen File f = new File(newPath); 402638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (newPath != null && f.isDirectory()) { 402710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 40281d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION, 40291d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood userWhere, whereArgs, null, null, null); 40301d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood try { 40311d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (cursor != null && cursor.moveToNext()) { 40321d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood oldPath = cursor.getString(1); 40331d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 40341d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } finally { 40351d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (cursor != null) cursor.close(); 40361d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 40371d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (oldPath != null) { 40387f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.remove(oldPath); 40391d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // first rename the row for the directory 404010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 40411d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood count = db.update(sGetTableAndWhereParam.table, initialValues, 40421d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood sGetTableAndWhereParam.where, whereArgs); 40431d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (count > 0) { 40449bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood // update the paths of any files and folders contained in the directory 40455461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen Object[] bindArgs = new Object[] {newPath, oldPath.length() + 1, 4046a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang oldPath + "/", oldPath + "0", 40479bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood // update bucket_display_name and bucket_id based on new path 40489bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood f.getName(), 40499bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood f.toString().toLowerCase().hashCode() 40509bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood }; 405110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 40525461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen db.execSQL("UPDATE files SET _data=?1||SUBSTR(_data, ?2)" + 40539bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood // also update bucket_display_name 40549bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood ",bucket_display_name=?6" + 40559bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood ",bucket_id=?7" + 4056a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync " WHERE _data >= ?3 AND _data < ?4;", 40575461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen bindArgs); 40581d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 40591d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 40601d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (count > 0 && !db.inTransaction()) { 40611d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood getContext().getContentResolver().notifyChange(uri, null); 40621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 406338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (f.getName().startsWith(".")) { 406438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen // the new directory name is hidden 406538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processNewNoMediaPath(helper, db, newPath); 406638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 40671d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood return count; 40681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 406938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) { 407038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processNewNoMediaPath(helper, db, newPath); 40711d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 40721d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 40731d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 4074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 4075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 4076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 4077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 4078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 40792658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST); 40802658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION); 40812658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen values.remove(MediaStore.Audio.Media.COMPILATION); 408207656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen 4083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Insert the artist into the artist table and remove it from 4084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // the input values 4085a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String artist = values.getAsString("artist"); 40866006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen values.remove("artist"); 4087a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (artist != null) { 4088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long artistRowId; 408910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> artistCache = helper.mArtistCache; 4090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized(artistCache) { 4091a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen Long temp = artistCache.get(artist); 4092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (temp == null) { 409310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen artistRowId = getKeyIdForName(helper, db, 409410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "artists", "artist_key", "artist", 4095acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen artist, artist, null, 0, null, artistCache, uri); 4096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 4097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project artistRowId = temp.longValue(); 4098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("artist_id", Integer.toString((int)artistRowId)); 4101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 410359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // Do the same for the album field. 4104a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String so = values.getAsString("album"); 41056006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen values.remove("album"); 4106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (so != null) { 4107801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String path = values.getAsString(MediaStore.MediaColumns.DATA); 410859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen int albumHash = 0; 41092658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (albumartist != null) { 41102658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumHash = albumartist.hashCode(); 41112658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else if (compilation != null && compilation.equals("1")) { 41122658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // nothing to do, hash already set 411359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } else { 41149289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (path == null) { 41159289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (match == AUDIO_MEDIA) { 41169289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen Log.w(TAG, "Possible multi row album name update without" 41179289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen + " path could give wrong album key"); 41189289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } else { 41199289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen //Log.w(TAG, "Specify path to avoid extra query"); 41209289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen Cursor c = query(uri, 41219289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen new String[] { MediaStore.Audio.Media.DATA}, 41229289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen null, null, null); 41239289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (c != null) { 41249289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen try { 41259289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen int numrows = c.getCount(); 41269289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (numrows == 1) { 41279289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen c.moveToFirst(); 41289289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen path = c.getString(0); 41299289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } else { 41309289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen Log.e(TAG, "" + numrows + " rows for " + uri); 41319289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 41329289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } finally { 41339289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen c.close(); 41349289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 41359289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 41369289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 41379289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 41389289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (path != null) { 41399289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen albumHash = path.substring(0, path.lastIndexOf('/')).hashCode(); 41409289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 414159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 41422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 4143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = so.toString(); 4144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long albumRowId; 414510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> albumCache = helper.mAlbumCache; 4146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized(albumCache) { 414759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen String cacheName = s + albumHash; 414859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen Long temp = albumCache.get(cacheName); 4149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (temp == null) { 415010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen albumRowId = getKeyIdForName(helper, db, 415110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "albums", "album_key", "album", 4152a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen s, cacheName, path, albumHash, artist, albumCache, uri); 4153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 4154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project albumRowId = temp.longValue(); 4155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("album_id", Integer.toString((int)albumRowId)); 4158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // don't allow the title_key field to be updated directly 4161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove("title_key"); 4162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // If the title field is modified, update the title_key 4163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project so = values.getAsString("title"); 4164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (so != null) { 4165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = so.toString(); 4166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("title_key", MediaStore.Audio.keyFor(s)); 4167e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen // do a final trim of the title, in case it started with the special 4168e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen // "sort first" character (ascii \001) 4169e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen values.remove("title"); 4170e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen values.put("title", s.trim()); 4171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 417310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 4174afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood count = db.update(sGetTableAndWhereParam.table, values, 4175afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood sGetTableAndWhereParam.where, whereArgs); 4176b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (genre != null) { 4177b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (count == 1 && match == AUDIO_MEDIA_ID) { 4178b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood long rowId = Long.parseLong(uri.getPathSegments().get(3)); 4179b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood updateGenre(rowId, genre); 4180b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } else { 4181b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // can't handle genres for bulk update or for non-audio files 4182b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Log.w(TAG, "ignoring genre in update: count = " 4183b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood + count + " match = " + match); 4184b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 4185b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 4186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 4189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 4190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 4191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 4192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 4193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 4194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Don't allow bucket id or display name to be updated directly. 4195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // The same names are used for both images and table columns, so 4196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we use the ImageColumns constants here. 4197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove(ImageColumns.BUCKET_ID); 4198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove(ImageColumns.BUCKET_DISPLAY_NAME); 4199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // If the data is being modified update the bucket values 4200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = values.getAsString(MediaColumns.DATA); 4201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (data != null) { 4202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project computeBucketValues(data, values); 4203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4204b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen computeTakenTime(values); 420510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 4206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.update(sGetTableAndWhereParam.table, values, 4207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 420801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // if this is a request from MediaScanner, DATA should contains file path 420901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // we only process update request from media scanner, otherwise the requests 421001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // could be duplicate. 421101a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) { 421210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 421301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen Cursor c = db.query(sGetTableAndWhereParam.table, 421401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen READY_FLAG_PROJECTION, sGetTableAndWhereParam.where, 421501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen whereArgs, null, null, null); 4216b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c != null) { 4217216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen try { 4218216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen while (c.moveToNext()) { 4219216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen long magic = c.getLong(2); 4220216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen if (magic == 0) { 4221216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen requestMediaThumbnail(c.getString(1), uri, 4222216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen MediaThumbRequest.PRIORITY_NORMAL, 0); 4223216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } 4224b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4225216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } finally { 4226216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c.close(); 4227b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4228b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4229b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4232f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen 4233f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 4234f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen String moveit = uri.getQueryParameter("move"); 4235f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (moveit != null) { 4236f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER; 4237f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (initialValues.containsKey(key)) { 4238f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen int newpos = initialValues.getAsInteger(key); 4239f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen List <String> segments = uri.getPathSegments(); 4240f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen long playlist = Long.valueOf(segments.get(3)); 4241f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen int oldpos = Integer.valueOf(segments.get(5)); 424210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen return movePlaylistEntry(helper, db, playlist, oldpos, newpos); 4243f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4244f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen throw new IllegalArgumentException("Need to specify " + key + 4245f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " when using 'move' parameter"); 4246f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4247f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // fall through 4248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 424910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 4250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.update(sGetTableAndWhereParam.table, initialValues, 4251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 4252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4255cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // in a transaction, the code that began the transaction should be taking 4256cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // care of notifications once it ends the transaction successfully 4257cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (count > 0 && !db.inTransaction()) { 4258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 4259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return count; 4261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 426310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db, 426410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long playlist, int from, int to) { 4265f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (from == to) { 4266f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen return 0; 4267f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4268f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.beginTransaction(); 42695e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé int numlines = 0; 4270f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen try { 427110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates += 3; 42724eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Cursor c = db.query("audio_playlists_map", 42734eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen new String [] {"play_order" }, 42744eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order", 42754eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen from + ",1"); 42764eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.moveToFirst(); 42774eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen int from_play_order = c.getInt(0); 42784eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.close(); 42794eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c = db.query("audio_playlists_map", 42804eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen new String [] {"play_order" }, 42814eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order", 42824eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen to + ",1"); 42834eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.moveToFirst(); 42844eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen int to_play_order = c.getInt(0); 42854eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.close(); 4286f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=-1" + 42874eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE play_order=" + from_play_order + 4288f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 4289f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // We could just run both of the next two statements, but only one of 4290f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // of them will actually do anything, so might as well skip the compile 4291f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // and execute steps. 4292f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (from < to) { 4293f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" + 42944eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE play_order<=" + to_play_order + 42954eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " AND play_order>" + from_play_order + 4296f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 4297f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen numlines = to - from + 1; 4298f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } else { 4299f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" + 43004eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE play_order>=" + to_play_order + 43014eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " AND play_order<" + from_play_order + 4302f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 4303f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen numlines = from - to + 1; 4304f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 43054eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=" + to_play_order + 4306f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " WHERE play_order=-1 AND playlist_id=" + playlist); 4307f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.setTransactionSuccessful(); 4308f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } finally { 4309f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.endTransaction(); 4310f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 43115e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé 43125e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI 43135e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé .buildUpon().appendEncodedPath(String.valueOf(playlist)).build(); 43145e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé // notifyChange() must be called after the database transaction is ended 43155e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé // or the listeners will read the old data in the callback 43165e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé getContext().getContentResolver().notifyChange(uri, null); 43175e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé 43185e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé return numlines; 4319f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4320f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen 4321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String[] openFileColumns = new String[] { 4322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns.DATA, 4323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 4324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 4326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public ParcelFileDescriptor openFile(Uri uri, String mode) 4327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throws FileNotFoundException { 432871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 4329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor pfd = null; 433071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 433171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) { 433271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // get album art for the specified media file 433371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen DatabaseHelper database = getDatabaseForUri(uri); 433471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (database == null) { 433571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw new IllegalStateException("Couldn't open database for " + uri); 433671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 433771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen SQLiteDatabase db = database.getReadableDatabase(); 43385fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 43395fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 43405fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 434171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 434271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen int songid = Integer.parseInt(uri.getPathSegments().get(3)); 434371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.setTables("audio_meta"); 434471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.appendWhere("_id=" + songid); 434571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen Cursor c = qb.query(db, 434671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen new String [] { 434771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen MediaStore.Audio.Media.DATA, 434871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen MediaStore.Audio.Media.ALBUM_ID }, 434971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen null, null, null, null, null); 435071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (c.moveToFirst()) { 435171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen String audiopath = c.getString(0); 435271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen int albumid = c.getInt(1); 435371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // Try to get existing album art for this album first, which 435471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // could possibly have been obtained from a different file. 435571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // If that fails, try to get it from this specific file. 435671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid); 435771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen try { 4358007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey pfd = openFileAndEnforcePathPermissionsHelper(newUri, mode); 435971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } catch (FileNotFoundException ex) { 436071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // That didn't work, now try to get it from the specific file 436134fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen pfd = getThumb(database, db, audiopath, albumid, null); 436271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 436371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 436471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen c.close(); 436571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return pfd; 436671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 436771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 4368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 4369007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey pfd = openFileAndEnforcePathPermissionsHelper(uri, mode); 4370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (FileNotFoundException ex) { 437171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (mode.contains("w")) { 437271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // if the file couldn't be created, we shouldn't extract album art 437371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw ex; 437471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 437571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 4376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) { 4377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Tried to open an album art file which does not exist. Regenerate. 4378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 4379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 4380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw ex; 4381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getReadableDatabase(); 43835fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 43845fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 43855fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 4386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 4387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int albumid = Integer.parseInt(uri.getPathSegments().get(3)); 438871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.setTables("audio_meta"); 4389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("album_id=" + albumid); 4390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = qb.query(db, 4391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new String [] { 4392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Media.DATA }, 4393a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen null, null, null, null, MediaStore.Audio.Media.TRACK); 439471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (c.moveToFirst()) { 4395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String audiopath = c.getString(0); 439634fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen pfd = getThumb(database, db, audiopath, albumid, uri); 4397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project c.close(); 4399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 440071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (pfd == null) { 440171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw ex; 440271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 4403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return pfd; 4405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4407007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** 4408007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey * Return the {@link MediaColumns#DATA} field for the given {@code Uri}. 4409007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey */ 4410007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private File queryForDataFile(Uri uri) throws FileNotFoundException { 4411007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey final Cursor cursor = query( 4412007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey uri, new String[] { MediaColumns.DATA }, null, null, null); 44138ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey if (cursor == null) { 44148ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey throw new FileNotFoundException("Missing cursor for " + uri); 44158ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey } 44168ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey 4417007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey try { 4418007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey switch (cursor.getCount()) { 4419007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey case 0: 4420007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new FileNotFoundException("No entry for " + uri); 4421007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey case 1: 4422007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey if (cursor.moveToFirst()) { 4423007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey return new File(cursor.getString(0)); 4424007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } else { 4425007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new FileNotFoundException("Unable to read entry for " + uri); 4426007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4427007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey default: 4428007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new FileNotFoundException("Multiple items at " + uri); 4429007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4430007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } finally { 4431007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey cursor.close(); 4432007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4433007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4434007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4435007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** 4436007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey * Replacement for {@link #openFileHelper(Uri, String)} which enforces any 4437007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey * permissions applicable to the path before returning. 4438007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey */ 4439007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode) 4440007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throws FileNotFoundException { 4441007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey final int modeBits = ContentResolver.modeToMode(uri, mode); 4442007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0; 4443007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4444f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey File file = queryForDataFile(uri); 4445007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey final String path; 4446007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey try { 4447007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey path = file.getCanonicalPath(); 4448007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } catch (IOException e) { 4449007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new IllegalArgumentException("Unable to resolve canonical path for " + file, e); 4450007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4451007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4452007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey if (path.startsWith(sExternalPath)) { 44533e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen Context c = getContext(); 44543e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) 44553e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen != PackageManager.PERMISSION_GRANTED) { 44563e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen c.enforceCallingOrSelfPermission( 44573e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen READ_EXTERNAL_STORAGE, "External path: " + path); 44583e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen } 4459007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4460007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey if (isWrite) { 44613e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 44623e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen != PackageManager.PERMISSION_GRANTED) { 44633e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen c.enforceCallingOrSelfPermission( 44643e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen WRITE_EXTERNAL_STORAGE, "External path: " + path); 44653e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen } 4466007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4467f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey 44689f635dd94465e4dc324c6c4c15d081d1b18b5693Jeff Sharkey // Bypass emulation layer when file is opened for reading, but only 4469dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey // when opening read-only and we have an exact match. 44709f635dd94465e4dc324c6c4c15d081d1b18b5693Jeff Sharkey if (modeBits == MODE_READ_ONLY) { 44719f635dd94465e4dc324c6c4c15d081d1b18b5693Jeff Sharkey file = Environment.maybeTranslateEmulatedPathToInternal(file); 4472f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey } 4473007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } else if (path.startsWith(sCachePath)) { 4474007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey getContext().enforceCallingOrSelfPermission( 4475007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey ACCESS_CACHE_FILESYSTEM, "Cache path: " + path); 447670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } else if (isWrite) { 447770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // don't write to non-cache, non-sdcard files. 447870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + file); 447970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } else { 448070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen checkWorldReadAccess(path); 4481007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4482007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4483007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey return ParcelFileDescriptor.open(file, modeBits); 4484007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4485007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 448670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen /** 448770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen * Check whether the path is a world-readable file 448870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen */ 448970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen private void checkWorldReadAccess(String path) throws FileNotFoundException { 449070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 449170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen try { 449270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen StructStat stat = Libcore.os.stat(path); 449370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen int accessBits = OsConstants.S_IROTH; 449470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen if (OsConstants.S_ISREG(stat.st_mode) && 449570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen ((stat.st_mode & accessBits) == accessBits)) { 449670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen checkLeadingPathComponentsWorldExecutable(path); 449770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen return; 449870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 449970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } catch (ErrnoException e) { 450070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // couldn't stat the file, either it doesn't exist or isn't 450170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // accessible to us 450270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 450370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 450470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + path); 450570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 450670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 450770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen private void checkLeadingPathComponentsWorldExecutable(String filePath) 450870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throws FileNotFoundException { 450970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen File parent = new File(filePath).getParentFile(); 451070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 451170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen int accessBits = OsConstants.S_IXOTH; 451270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 451370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen while (parent != null) { 451470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen if (! parent.exists()) { 451570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // parent dir doesn't exist, give up 451670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("access denied"); 451770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 451870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen try { 451970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen StructStat stat = Libcore.os.stat(parent.getPath()); 452070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen if ((stat.st_mode & accessBits) != accessBits) { 452170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // the parent dir doesn't have the appropriate access 452270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + filePath); 452370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 452470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } catch (ErrnoException e1) { 452570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // couldn't stat() parent 452670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + filePath); 452770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 452870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen parent = parent.getParentFile(); 452970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 453070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 453170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 4532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private class ThumbData { 453310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper; 4534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db; 4535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String path; 4536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long album_id; 4537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri albumart_uri; 4538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 454010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db, 454110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen String path, long album_id) { 45428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber synchronized (mPendingThumbs) { 45438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (mPendingThumbs.contains(path)) { 45448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // There's already a request to make an album art thumbnail 45458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // for this audio file in the queue. 45468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return; 45478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 45488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 45498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber mPendingThumbs.add(path); 45508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 45518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 4552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ThumbData d = new ThumbData(); 455310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen d.helper = helper; 4554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.db = db; 4555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.path = path; 4556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.album_id = album_id; 4557a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id); 45588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 45598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Instead of processing thumbnail requests in the order they were 45608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // received we instead process them stack-based, i.e. LIFO. 45618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // The idea behind this is that the most recently requested thumbnails 45628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // are most likely the ones still in the user's view, whereas those 45638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // requested earlier may have already scrolled off. 45648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber synchronized (mThumbRequestStack) { 45658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber mThumbRequestStack.push(d); 45668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 45678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 45688a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Trigger the handler. 4569b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB); 4570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project msg.sendToTarget(); 4571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4573f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen //Return true if the artPath is the dir as it in mExternalStoragePaths 4574f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen //for multi storage support 4575f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen private static boolean isRootStorageDir(String artPath) { 4576f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen for ( int i = 0; i < mExternalStoragePaths.length; i++) { 4577f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen if ((mExternalStoragePaths[i] != null) && 4578f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen (artPath.equalsIgnoreCase(mExternalStoragePaths[i]))) 4579f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen return true; 4580f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen } 4581f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen return false; 4582f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen } 4583f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen 45848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Extract compressed image data from the audio file itself or, if that fails, 45858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // look for a file "AlbumArt.jpg" in the containing directory. 45868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private static byte[] getCompressedAlbumArt(Context context, String path) { 45878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber byte[] compressed = null; 4588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 4590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File f = new File(path); 4591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f, 4592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor.MODE_READ_ONLY); 4593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 45948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber MediaScanner scanner = new MediaScanner(context); 45958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber compressed = scanner.extractAlbumArt(pfd.getFileDescriptor()); 4596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project pfd.close(); 4597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4598d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // If no embedded art exists, look for a suitable image file in the 45993f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen // same directory as the media file, except if that directory is 46003f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen // is the root directory of the sd card or the download directory. 4601d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // We look for, in order of preference: 4602d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 0 AlbumArt.jpg 4603d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 1 AlbumArt*Large.jpg 4604d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 2 Any other jpg image with 'albumart' anywhere in the name 4605d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 3 Any other jpg image 4606d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 4 any other png image 46078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (compressed == null && path != null) { 4608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int lastSlash = path.lastIndexOf('/'); 4609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lastSlash > 0) { 4610d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 46113f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen String artPath = path.substring(0, lastSlash); 46120fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen String dwndir = Environment.getExternalStoragePublicDirectory( 46132f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); 4614d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 4615d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String bestmatch = null; 4616d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen synchronized (sFolderArtMap) { 4617d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (sFolderArtMap.containsKey(artPath)) { 4618d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = sFolderArtMap.get(artPath); 4619f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen } else if (!isRootStorageDir(artPath) && 4620ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen !artPath.equalsIgnoreCase(dwndir)) { 4621d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen File dir = new File(artPath); 4622d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String [] entrynames = dir.list(); 4623d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (entrynames == null) { 4624d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen return null; 4625d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4626d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = null; 4627d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen int matchlevel = 1000; 4628d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen for (int i = entrynames.length - 1; i >=0; i--) { 4629d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String entry = entrynames[i].toLowerCase(); 4630d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (entry.equals("albumart.jpg")) { 4631d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4632d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen break; 4633d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.startsWith("albumart") 4634d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && entry.endsWith("large.jpg") 4635d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && matchlevel > 1) { 4636d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4637d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 1; 4638d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.contains("albumart") 4639d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && entry.endsWith(".jpg") 4640d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && matchlevel > 2) { 4641d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4642d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 2; 4643d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.endsWith(".jpg") && matchlevel > 3) { 4644d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4645d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 3; 4646d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.endsWith(".png") && matchlevel > 4) { 4647d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4648d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 4; 4649d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4650d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4651d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // note that this may insert null if no album art was found 4652d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen sFolderArtMap.put(artPath, bestmatch); 4653d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4654d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4655d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 4656d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (bestmatch != null) { 46573f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen File file = new File(artPath, bestmatch); 4658d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (file.exists()) { 4659d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen FileInputStream stream = null; 4660d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen try { 46612c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen compressed = new byte[(int)file.length()]; 4662d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream = new FileInputStream(file); 4663d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream.read(compressed); 4664d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } catch (IOException ex) { 4665d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen compressed = null; 46662c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen } catch (OutOfMemoryError ex) { 46672c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen Log.w(TAG, ex); 46682c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen compressed = null; 4669d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } finally { 4670d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (stream != null) { 4671d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream.close(); 4672d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 46788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (IOException e) { 46798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 4680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 46818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return compressed; 46828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 4683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 46848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Return a URI to write the album art to and update the database as necessary. 468510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) { 46868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Uri out = null; 46878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // TODO: this could be done more efficiently with a call to db.replace(), which 46888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // replaces or inserts as needed, making it unnecessary to query() first. 46898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (albumart_uri != null) { 4690801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA }, 46918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber null, null, null); 4692d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson try { 4693d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (c != null && c.moveToFirst()) { 4694d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson String albumart_path = c.getString(0); 4695d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (ensureFileExists(albumart_path)) { 4696d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson out = albumart_uri; 4697d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } 4698d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } else { 4699d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson albumart_uri = null; 4700d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } 4701d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } finally { 4702d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (c != null) { 4703d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson c.close(); 4704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 470671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 470771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (albumart_uri == null){ 47088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber ContentValues initialValues = new ContentValues(); 47098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber initialValues.put("album_id", album_id); 47108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 47118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER); 471210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 4713801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values); 47148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (rowId > 0) { 47158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber out = ContentUris.withAppendedId(ALBUMART_URI, rowId); 471650c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen // ensure the parent directory exists 471750c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen String albumart_path = values.getAsString(MediaStore.MediaColumns.DATA); 471850c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen ensureFileExists(albumart_path); 4719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 47208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (IllegalStateException ex) { 47218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Log.e(TAG, "error creating album thumb file"); 47228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return out; 47258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Write out the album art to the output URI, recompresses the given Bitmap 47288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // if necessary, otherwise writes the compressed data. 47298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private void writeAlbumArt( 473020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) throws IOException { 473120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen OutputStream outstream = null; 47328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 473320e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen outstream = getContext().getContentResolver().openOutputStream(out); 47348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (!need_to_recompress) { 47368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // No need to recompress here, just write out the original 47378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // compressed data here. 47388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber outstream.write(compressed); 47398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } else { 474020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen if (!bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream)) { 474120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen throw new IOException("failed to compress bitmap"); 474220e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } 4743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 474420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } finally { 474520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen IoUtils.closeQuietly(outstream); 4746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 47478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 474934fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen private ParcelFileDescriptor getThumb(DatabaseHelper helper, SQLiteDatabase db, String path, 475034fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen long album_id, Uri albumart_uri) { 475171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen ThumbData d = new ThumbData(); 475234fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen d.helper = helper; 475371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.db = db; 475471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.path = path; 475571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.album_id = album_id; 475671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.albumart_uri = albumart_uri; 475771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return makeThumbInternal(d); 475871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 475971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 476071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen private ParcelFileDescriptor makeThumbInternal(ThumbData d) { 47618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber byte[] compressed = getCompressedAlbumArt(getContext(), d.path); 4762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 47638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (compressed == null) { 476471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 47658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Bitmap bm = null; 47688a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber boolean need_to_recompress = true; 47698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 47718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // get the size of the bitmap 47728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber BitmapFactory.Options opts = new BitmapFactory.Options(); 47738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inJustDecodeBounds = true; 47748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inSampleSize = 1; 47758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts); 47768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // request a reasonably sized output image 477870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann final Resources r = getContext().getResources(); 477970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size); 478070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) { 47818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.outHeight /= 2; 47828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.outWidth /= 2; 47838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inSampleSize *= 2; 47848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (opts.inSampleSize == 1) { 47878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // The original album art was of proper size, we won't have to 47888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // recompress the bitmap later. 47898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber need_to_recompress = false; 47908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } else { 47918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // get the image for real now 47928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inJustDecodeBounds = false; 47938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inPreferredConfig = Bitmap.Config.RGB_565; 47948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts); 47958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (bm != null && bm.getConfig() == null) { 4797a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false); 4798a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (nbm != null && nbm != bm) { 4799a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm.recycle(); 4800a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm = nbm; 4801a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } 48028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 48038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 48048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (Exception e) { 48058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 48068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 48078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (need_to_recompress && bm == null) { 480871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 48098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 48108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 481171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (d.albumart_uri == null) { 481271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // this one doesn't need to be saved (probably a song with an unknown album), 481371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // so stick it in a memory file and return that 481471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen try { 481524001394f571b1f0378840cbf299288e4df10508Bjorn Bringert return ParcelFileDescriptor.fromData(compressed, "albumthumb"); 481671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } catch (IOException e) { 481771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 481871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } else { 4819a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // This one needs to actually be saved on the sd card. 4820a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // This is wrapped in a transaction because there are various things 4821a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // that could go wrong while generating the thumbnail, and we only want 4822a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // to update the database when all steps succeeded. 4823a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.beginTransaction(); 482420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen Uri out = null; 482520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen ParcelFileDescriptor pfd = null; 4826a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen try { 482720e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri); 4828a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen 4829a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (out != null) { 4830a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen writeAlbumArt(need_to_recompress, out, compressed, bm); 4831a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen getContext().getContentResolver().notifyChange(MEDIA_URI, null); 483220e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen pfd = openFileHelper(out, "r"); 4833a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.setTransactionSuccessful(); 4834a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen return pfd; 4835a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } 483620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } catch (IOException ex) { 4837a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // do nothing, just return null below 4838a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } catch (UnsupportedOperationException ex) { 4839a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // do nothing, just return null below 4840a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } finally { 4841a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.endTransaction(); 4842a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (bm != null) { 4843a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm.recycle(); 484471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 484520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen if (pfd == null && out != null) { 484620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // Thumbnail was not written successfully, delete the entry that refers to it. 484720e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // Note that this only does something if getAlbumArtOutputUri() reused an 484820e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // existing entry from the database. If a new entry was created, it will 484920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // have been rolled back as part of backing out the transaction. 485020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen getContext().getContentResolver().delete(out, null, null); 485120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } 485271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 48538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 485471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 4855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 4858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Look up the artist or album entry for the given name, creating that entry 4859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * if it does not already exists. 4860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db The database 4861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param table The table to store the key/name pair in. 4862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param keyField The name of the key-column 4863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param nameField The name of the name-column 4864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param rawName The name that the calling app was trying to insert into the database 486559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param cacheName The string that will be inserted in to the cache 486659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param path The full path to the file being inserted in to the audio table 486759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param albumHash A hash to distinguish between different albums of the same name 4868a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen * @param artist The name of the artist, if known 4869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param cache The cache to add this entry to 4870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param srcuri The Uri that prompted the call to this method, used for determining whether this is 4871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * the internal or external database 4872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return The row ID for this artist/album, or -1 if the provided name was invalid 4873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 487410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db, 487510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen String table, String keyField, String nameField, 487659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen String rawName, String cacheName, String path, int albumHash, 4877a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String artist, HashMap<String, Long> cache, Uri srcuri) { 4878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long rowId; 4879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rawName == null || rawName.length() == 0) { 488151cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen rawName = MediaStore.UNKNOWN_STRING; 4882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String k = MediaStore.Audio.keyFor(rawName); 4884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (k == null) { 488651cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen // shouldn't happen, since we only get null keys for null inputs 488751cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen Log.e(TAG, "null key", new Exception()); 4888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return -1; 4889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 489159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen boolean isAlbum = table.equals("albums"); 4892e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName); 489359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 48942658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // To distinguish same-named albums, we append a hash. The hash is based 48952658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // on the "album artist" tag if present, otherwise on the "compilation" tag 48962658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // if present, otherwise on the path. 489759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // Ideally we would also take things like CDDB ID in to account, so 489859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // we can group files from the same album that aren't in the same 489959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // folder, but this is a quick and easy start that works immediately 490059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // without requiring support from the mp3, mp4 and Ogg meta data 490159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // readers, as long as the albums are in different folders. 4902a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (isAlbum) { 490359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen k = k + albumHash; 4904a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (isUnknown) { 4905a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen k = k + artist; 4906a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen } 490759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 490859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 4909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String [] selargs = { k }; 491010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 4911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null); 4912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 4914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (c.getCount()) { 4915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case 0: { 4916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // insert new entry into table 4917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues otherValues = new ContentValues(); 4918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project otherValues.put(keyField, k); 4919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project otherValues.put(nameField, rawName); 492010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 4921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert(table, "duration", otherValues); 492259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (path != null && isAlbum && ! isUnknown) { 4923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // We just inserted a new album. Now create an album art thumbnail for it. 492410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen makeThumbAsync(helper, db, path, rowId); 4925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 4927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = srcuri.toString().substring(16, 24); // extract internal/external 4928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId); 4929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 4930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case 1: { 4934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Use the existing entry 4935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project c.moveToFirst(); 4936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = c.getLong(0); 4937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Determine whether the current rawName is better than what's 4939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // currently stored in the table, and update the table if it is. 4940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String currentFancyName = c.getString(2); 4941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String bestName = makeBestName(rawName, currentFancyName); 4942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!bestName.equals(currentFancyName)) { 4943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // update the table with the new name 4944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues newValues = new ContentValues(); 4945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newValues.put(nameField, bestName); 494610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 4947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null); 4948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = srcuri.toString().substring(16, 24); // extract internal/external 4949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId); 4950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 4951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 4955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // corrupt database 4956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "Multiple entries in table " + table + " for key " + k); 4957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = -1; 4958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 4961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (c != null) c.close(); 4962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 496459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (cache != null && ! isUnknown) { 496559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen cache.put(cacheName, rowId); 4966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return rowId; 4968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 4971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Returns the best string to use for display, given two names. 4972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Note that this function does not necessarily return either one 4973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * of the provided names; it may decide to return a better alternative 4974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * (for example, specifying the inputs "Police" and "Police, The" will 4975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * return "The Police") 4976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 4977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * The basic assumptions are: 4978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - longer is better ("The police" is better than "Police") 4979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - prefix is better ("The Police" is better than "Police, The") 4980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - accents are better ("Motörhead" is better than "Motorhead") 4981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 4982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param one The first of the two names to consider 4983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param two The last of the two names to consider 4984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return The actual name to use 4985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 4986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String makeBestName(String one, String two) { 4987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name; 4988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Longer names are usually better. 4990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (one.length() > two.length()) { 4991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = one; 4992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 4993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Names with accents are usually better, and conveniently sort later 4994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) { 4995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = one; 4996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 4997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = two; 4998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Prefixes are better than postfixes. 5002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (name.endsWith(", the") || name.endsWith(",the") || 5003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name.endsWith(", an") || name.endsWith(",an") || 5004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name.endsWith(", a") || name.endsWith(",a")) { 5005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String fix = name.substring(1 + name.lastIndexOf(',')); 5006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = fix.trim() + " " + name.substring(0, name.lastIndexOf(',')); 5007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // TODO: word-capitalize the resulting name 5010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return name; 5011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 5015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Looks up the database based on the given URI. 5016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param uri The requested URI 5018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @returns the database for the given URI 5019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 5020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private DatabaseHelper getDatabaseForUri(Uri uri) { 5021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 50225619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen if (uri.getPathSegments().size() >= 1) { 5023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return mDatabases.get(uri.getPathSegments().get(0)); 5024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 5027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5029fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn static boolean isMediaDatabaseName(String name) { 5030fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (INTERNAL_DATABASE_NAME.equals(name)) { 5031fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5032fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5033fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (EXTERNAL_DATABASE_NAME.equals(name)) { 5034fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5035fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5036168d49e39a356cdbd0fa04c356e3480e413d6f34kwangjung.kim if (name.startsWith("external-") && name.endsWith(".db")) { 5037fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5038fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5039fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return false; 5040fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5041fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn 5042fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn static boolean isInternalMediaDatabaseName(String name) { 5043fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (INTERNAL_DATABASE_NAME.equals(name)) { 5044fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5045fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5046fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return false; 5047fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5048fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn 5049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 5050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Attach the database for a volume (internal or external). 5051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Does nothing if the volume is already attached, otherwise 5052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * checks the volume ID and sets up the corresponding database. 5053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}. 5055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return the content URI of the attached volume. 5056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 5057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Uri attachVolume(String volume) { 505875392afde5217038b0c98077758bb9329c2431b2Jeff Brown if (Binder.getCallingPid() != Process.myPid()) { 5059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new SecurityException( 5060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Opening and closing databases not allowed."); 5061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 5064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mDatabases.get(volume) != null) { // Already attached 5065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Uri.parse("content://media/" + volume); 5066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5068993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Context context = getContext(); 506910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper; 5070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_VOLUME.equals(volume)) { 507110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true, 5072fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn false, mObjectRemovedCallback); 5073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (EXTERNAL_VOLUME.equals(volume)) { 5074993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (Environment.isExternalStorageRemovable()) { 50759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood String path = mExternalStoragePaths[0]; 5076993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood int volumeID = FileUtils.getFatVolumeId(path); 5077993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID); 5078993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood 5079ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // Must check for failure! 5080ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // If the volume is not (yet) mounted, this will create a new 5081ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // external-ffffffff.db database instead of the one we expect. Then, if 5082ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // android.process.media is later killed and respawned, the real external 5083ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // database will be attached, containing stale records, or worse, be empty. 5084ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick if (volumeID == -1) { 5085ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick String state = Environment.getExternalStorageState(); 5086ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick if (Environment.MEDIA_MOUNTED.equals(state) || 5087ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { 5088ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // This may happen if external storage was _just_ mounted. It may also 5089ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // happen if the volume ID is _actually_ 0xffffffff, in which case it 5090ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // must be changed since FileUtils::getFatVolumeId doesn't allow for 5091ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // that. It may also indicate that FileUtils::getFatVolumeId is broken 5092ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // (missing ioctl), which is also impossible to disambiguate. 5093ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick Log.e(TAG, "Can't obtain external volume ID even though it's mounted."); 5094ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick } else { 5095ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick Log.i(TAG, "External volume is not (yet) mounted, cannot attach."); 5096ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick } 5097ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick 5098ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick throw new IllegalArgumentException("Can't obtain external volume ID for " + 5099ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick volume + " volume."); 5100ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick } 5101ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick 5102993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // generate database name based on volume ID 5103993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood String dbName = "external-" + Integer.toHexString(volumeID) + ".db"; 510410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper = new DatabaseHelper(context, dbName, false, 5105fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn false, mObjectRemovedCallback); 5106993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood mVolumeId = volumeID; 5107993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 5108993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // external database name should be EXTERNAL_DATABASE_NAME 5109993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // however earlier releases used the external-XXXXXXXX.db naming 5110993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // for devices without removable storage, and in that case we need to convert 5111993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // to this new convention 5112993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME); 5113993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (!dbFile.exists()) { 5114993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // find the most recent external database and rename it to 5115993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // EXTERNAL_DATABASE_NAME, and delete any other older 5116993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // external database files 5117993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File recentDbFile = null; 5118993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood for (String database : context.databaseList()) { 5119ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim if (database.startsWith("external-") && database.endsWith(".db")) { 5120993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File file = context.getDatabasePath(database); 5121993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile == null) { 5122993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood recentDbFile = file; 5123993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else if (file.lastModified() > recentDbFile.lastModified()) { 5124ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim context.deleteDatabase(recentDbFile.getName()); 5125993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood recentDbFile = file; 5126993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 5127ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim context.deleteDatabase(file.getName()); 5128993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5129993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5130993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5131993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile != null) { 5132993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile.renameTo(dbFile)) { 5133993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Log.d(TAG, "renamed database " + recentDbFile.getName() + 5134993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood " to " + EXTERNAL_DATABASE_NAME); 5135993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 5136993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Log.e(TAG, "Failed to rename database " + recentDbFile.getName() + 5137993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood " to " + EXTERNAL_DATABASE_NAME); 5138993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // This shouldn't happen, but if it does, continue using 5139993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // the file under its old name 5140993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood dbFile = recentDbFile; 5141993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5142993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5143993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME 5144993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 514510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper = new DatabaseHelper(context, dbFile.getName(), false, 5146fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn false, mObjectRemovedCallback); 5147993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 5149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException("There is no volume named " + volume); 5150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 515210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen mDatabases.put(volume, helper); 5153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 515410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (!helper.mInternal) { 5155ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // create default directories (only happens on first boot) 515610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen createDefaultFolders(helper, helper.getWritableDatabase()); 5157ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 5158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // clean up stray album art files: delete every file not in the database 51599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles(); 5160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashSet<String> fileSet = new HashSet(); 5161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; files != null && i < files.length; i++) { 5162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fileSet.add(files[i].getPath()); 5163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, 5166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null); 5167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 5168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor != null && cursor.moveToNext()) { 5169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fileSet.remove(cursor.getString(0)); 5170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 5172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (cursor != null) cursor.close(); 5173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Iterator<String> iterator = fileSet.iterator(); 5176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (iterator.hasNext()) { 5177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String filename = iterator.next(); 5178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename); 5179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new File(filename).delete(); 5180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume); 5185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Uri.parse("content://media/" + volume); 5186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 5189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Detach the database for a volume (must be external). 5190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Does nothing if the volume is already detached, otherwise 5191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * closes the database and sends a notification to listeners. 5192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param uri The content URI of the volume, as returned by {@link #attachVolume} 5194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 5195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private void detachVolume(Uri uri) { 519675392afde5217038b0c98077758bb9329c2431b2Jeff Brown if (Binder.getCallingPid() != Process.myPid()) { 5197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new SecurityException( 5198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Opening and closing databases not allowed."); 5199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = uri.getPathSegments().get(0); 5202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_VOLUME.equals(volume)) { 5203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 5204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Deleting the internal volume is not allowed"); 5205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (!EXTERNAL_VOLUME.equals(volume)) { 5206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException( 5207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "There is no volume named " + volume); 5208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 5211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = mDatabases.get(volume); 5212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) return; 5213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 5215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // touch the database file to show it is most recently used 5216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(database.getReadableDatabase().getPath()); 5217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file.setLastModified(System.currentTimeMillis()); 5218e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood } catch (Exception e) { 5219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "Can't touch database file", e); 5220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mDatabases.remove(volume); 5223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project database.close(); 5224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 5227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume); 5228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static String TAG = "MediaProvider"; 5231ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer private static final boolean LOCAL_LOGV = false; 5232971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn 5233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String INTERNAL_DATABASE_NAME = "internal.db"; 5234993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood private static final String EXTERNAL_DATABASE_NAME = "external.db"; 5235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // maximum number of cached external databases to keep 5237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int MAX_EXTERNAL_DATABASES = 3; 5238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Delete databases that have not been used in two months 5240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60) 5241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final long OBSOLETE_DATABASE_DB = 5184000000L; 5242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private HashMap<String, DatabaseHelper> mDatabases; 5244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Handler mThumbHandler; 5246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // name of the volume currently being scanned by the media scanner (or null) 5248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String mMediaScannerVolume; 5249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 52500027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // current FAT volume ID 5251993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood private int mVolumeId = -1; 52520027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 5253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final String INTERNAL_VOLUME = "internal"; 5254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final String EXTERNAL_VOLUME = "external"; 5255268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs"; 5256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // path for writing contents of in memory temp database 5258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String mTempDatabasePath; 5259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 52601717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS 526116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood // are stored in the "files" table, so do not renumber them unless you also add 52621717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood // a corresponding database upgrade step for it. 5263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_MEDIA = 1; 5264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_MEDIA_ID = 2; 5265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_THUMBNAILS = 3; 5266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_THUMBNAILS_ID = 4; 5267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA = 100; 5269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID = 101; 5270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_GENRES = 102; 5271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_GENRES_ID = 103; 5272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104; 5273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105; 5274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES = 106; 5275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES_ID = 107; 5276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES_ID_MEMBERS = 108; 5277bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen private static final int AUDIO_GENRES_ALL_MEMBERS = 109; 5278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS = 110; 5279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID = 111; 5280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112; 5281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113; 5282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS = 114; 5283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS_ID = 115; 5284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMS = 116; 5285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMS_ID = 117; 5286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS_ID_ALBUMS = 118; 5287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMART = 119; 5288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMART_ID = 120; 528971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen private static final int AUDIO_ALBUMART_FILE_ID = 121; 5290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VIDEO_MEDIA = 200; 5292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VIDEO_MEDIA_ID = 201; 5293b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int VIDEO_THUMBNAILS = 202; 5294b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int VIDEO_THUMBNAILS_ID = 203; 5295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VOLUMES = 300; 5297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VOLUMES_ID = 301; 5298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5299a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_LEGACY = 400; 5300a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_BASIC = 401; 5301a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_FANCY = 402; 5302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int MEDIA_SCANNER = 500; 5304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 53050027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen private static final int FS_ID = 600; 5306704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen private static final int VERSION = 601; 53070027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 530816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood private static final int FILES = 700; 530916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood private static final int FILES_ID = 701; 5310a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu 5311e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood // Used only by the MTP implementation 5312e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECTS = 702; 5313e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECTS_ID = 703; 5314e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECT_REFERENCES = 704; 5315819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood // UsbReceiver calls insert() and delete() with this URI to tell us 5316819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood // when MTP is connected and disconnected 5317819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood private static final int MTP_CONNECTED = 705; 5318b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 5319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final UriMatcher URI_MATCHER = 5320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new UriMatcher(UriMatcher.NO_MATCH); 5321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5322b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final String[] ID_PROJECTION = new String[] { 5323b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns._ID 5324b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen }; 5325b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 53261d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood private static final String[] PATH_PROJECTION = new String[] { 53271d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood MediaStore.MediaColumns._ID, 53281d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood MediaStore.MediaColumns.DATA, 53291d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood }; 53301d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 5331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String[] MIME_TYPE_PROJECTION = new String[] { 5332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns._ID, // 0 5333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns.MIME_TYPE, // 1 5334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 5335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5336b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final String[] READY_FLAG_PROJECTION = new String[] { 5337b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns._ID, 5338b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns.DATA, 5339b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Images.Media.MINI_THUMB_MAGIC 5340b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen }; 5341b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 5342e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood private static final String OBJECT_REFERENCES_QUERY = 5343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map" 5344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?" 5345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER; 5346e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 5347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static 5348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 5349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA); 5350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID); 5351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS); 5352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID); 5353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA); 5355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID); 5356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES); 5357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID); 5358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS); 5359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID); 5360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES); 5361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID); 5362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS); 5363bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS); 5364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS); 5365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID); 5366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS); 5367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID); 5368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS); 5369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID); 5370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS); 5371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS); 5372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID); 5373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART); 5374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID); 537571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID); 5376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA); 5378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID); 5379b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS); 5380b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID); 5381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER); 5383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 53840027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen URI_MATCHER.addURI("media", "*/fs_id", FS_ID); 5385704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen URI_MATCHER.addURI("media", "*/version", VERSION); 53860027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 5387819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED); 5388819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood 5389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*", VOLUMES_ID); 5390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", null, VOLUMES); 5391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5392b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // Used by MTP implementation 539316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood URI_MATCHER.addURI("media", "*/file", FILES); 539416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood URI_MATCHER.addURI("media", "*/file/#", FILES_ID); 5395e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS); 5396e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID); 5397e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES); 5398b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 5399a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen /** 5400a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen * @deprecated use the 'basic' or 'fancy' search Uris instead 5401a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen */ 5402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY, 5403a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_LEGACY); 5404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*", 5405a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_LEGACY); 5406a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 5407a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // used for search suggestions 5408a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY, 5409a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_BASIC); 5410a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY + 5411a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "/*", AUDIO_SEARCH_BASIC); 5412a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 5413a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // used by the music app's search activity 5414a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY); 5415a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY); 5416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 541710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen 541810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen @Override 541910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 542010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Collection<DatabaseHelper> foo = mDatabases.values(); 542110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen for (DatabaseHelper dbh: foo) { 5422988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen writer.println(dump(dbh, true)); 5423988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5424988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen writer.flush(); 5425988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5426988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 5427988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen private String dump(DatabaseHelper dbh, boolean dumpDbLog) { 5428988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen StringBuilder s = new StringBuilder(); 5429988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mName); 5430988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(": "); 5431988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen SQLiteDatabase db = dbh.getReadableDatabase(); 5432988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (db == null) { 5433988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("null"); 5434988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } else { 5435988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("version " + db.getVersion() + ", "); 5436988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null); 5437988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen try { 5438988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (c != null && c.moveToFirst()) { 5439988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen int num = c.getInt(0); 5440988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(num + " rows, "); 5441988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } else { 5442988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("couldn't get row count, "); 5443988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5444988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } finally { 5445988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (c != null) { 5446988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen c.close(); 5447988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5448988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5449988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumInserts + " inserts, "); 5450988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumUpdates + " updates, "); 5451988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumDeletes + " deletes, "); 5452988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumQueries + " queries, "); 5453988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (dbh.mScanStartTime != 0) { 5454988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("scan started " + DateUtils.formatDateTime(getContext(), 5455988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen dbh.mScanStartTime / 1000, 5456988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen DateUtils.FORMAT_SHOW_DATE 5457988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen | DateUtils.FORMAT_SHOW_TIME 5458988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen | DateUtils.FORMAT_ABBREV_ALL)); 5459988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen long now = dbh.mScanStopTime; 5460988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (now < dbh.mScanStartTime) { 5461988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen now = SystemClock.currentTimeMicro(); 5462988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5463988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(" (" + DateUtils.formatElapsedTime( 5464988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen (now - dbh.mScanStartTime) / 1000000) + ")"); 5465988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (dbh.mScanStopTime < dbh.mScanStartTime) { 5466988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (mMediaScannerVolume != null && 5467988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen dbh.mName.startsWith(mMediaScannerVolume)) { 5468988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(" (ongoing)"); 546910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } else { 5470988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(" (scanning " + mMediaScannerVolume + ")"); 5471988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5472988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5473988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5474988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (dumpDbLog) { 5475988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen c = db.query("log", new String[] {"time", "message"}, 5476f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen null, null, null, null, "rowid"); 5477988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen try { 5478988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (c != null) { 5479988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen while (c.moveToNext()) { 5480988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen String when = c.getString(0); 5481988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen String msg = c.getString(1); 5482988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("\n" + when + " : " + msg); 5483988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 548410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 548510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } finally { 548610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (c != null) { 548710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen c.close(); 548810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 548910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 549010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 549110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 5492988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen return s.toString(); 549310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 5494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project} 5495