MediaProvider.java revision 3fa4c1f0f2d9631aa41567b6c2efb2716421ba40
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.Handler; 64ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread; 65702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message; 66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor; 67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process; 68d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException; 6910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.os.SystemClock; 70c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageManager; 711f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwoodimport android.os.storage.StorageVolume; 72ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager; 73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns; 74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore; 75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio; 764eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissenimport android.provider.MediaStore.Audio.Playlists; 77a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Linimport android.provider.MediaStore.Files; 7870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns; 79702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images; 8070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns; 81702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns; 82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video; 83702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils; 8410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils; 85702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log; 86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 87ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.ErrnoException; 88ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.IoUtils; 89ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.Libcore; 90ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.OsConstants; 91ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.StructStat; 92ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkey 93702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File; 9410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor; 95702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream; 96702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException; 97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException; 98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream; 9910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter; 100cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList; 10110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection; 102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap; 103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet; 104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator; 105f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List; 10638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Locale; 107b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue; 1088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack; 109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/** 111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details. 112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the 113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index). The content visible at content://media/external/... 114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card. 115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider { 117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final Uri MEDIA_URI = Uri.parse("content://media"); 118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart"); 119b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int ALBUM_THUMB = 1; 120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int IMAGE_THUMB = 2; 121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>(); 123d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>(); 124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 125007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** Resolved canonical path to external storage. */ 126007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private static final String sExternalPath; 127007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** Resolved canonical path to cache storage. */ 128007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private static final String sCachePath; 1295943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen /** Resolved canonical path to legacy storage. */ 1305943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen private static final String sLegacyPath; 131007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 132007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey static { 133007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey try { 134007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey sExternalPath = Environment.getExternalStorageDirectory().getCanonicalPath(); 135007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey sCachePath = Environment.getDownloadCacheDirectory().getCanonicalPath(); 1365943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen sLegacyPath = Environment.getLegacyExternalStorageDirectory().getCanonicalPath(); 137007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } catch (IOException e) { 138007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new RuntimeException("Unable to resolve canonical paths", e); 139007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 140007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 141007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 1425d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey private StorageManager mStorageManager; 1435d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey 1447f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // In memory cache of path<->id mappings, to speed up inserts during media scan 1457f36494e085c26c69cd5925e54028822025eff29Marco Nelissen HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>(); 1467f36494e085c26c69cd5925e54028822025eff29Marco Nelissen 1478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // A HashSet of paths that are pending creation of album art thumbnails. 1488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private HashSet mPendingThumbs = new HashSet(); 1498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 1508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // A Stack of outstanding thumbnail requests. 1518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private Stack mThumbRequestStack = new Stack(); 1528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 15320434e032e498b716f87cce2f23dd646819218bfRay Chen // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest. 15420434e032e498b716f87cce2f23dd646819218bfRay Chen private MediaThumbRequest mCurrentThumbRequest = null; 155b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private PriorityQueue<MediaThumbRequest> mMediaThumbQueue = 156b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL, 157b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaThumbRequest.getComparator()); 158b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 159f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood private boolean mCaseInsensitivePaths; 1609be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood private static String[] mExternalStoragePaths; 16117ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood 162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // For compatibility with the approximately 0 apps that used mediaprovider search in 163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // releases 1.0, 1.1 or 1.5 164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsLegacy = new String[] { 165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 166a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist + 168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album + 169a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE " + R.drawable.ic_search_category_music_song + " END END" + 170a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1, 171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2, 172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, 173a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY, 174a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE when grouporder=1 THEN data1 ELSE artist END AS data1", 175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE when grouporder=1 THEN data2 ELSE " + 176a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2", 177a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "match as ar", 178a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen SearchManager.SUGGEST_COLUMN_INTENT_DATA, 179a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "grouporder", 180ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that 181ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen // column is not available here, and the list is already sorted. 182a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 183a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsFancy = new String[] { 184a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 185a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 186a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Artists.ARTIST, 187a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Albums.ALBUM, 188a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.TITLE, 189a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "data1", 190a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "data2", 191a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 19263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath // If this array gets changed, please update the constant below to point to the correct item. 193a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsBasic = new String[] { 194a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 195a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 196a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist + 197a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album + 198a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE " + R.drawable.ic_search_category_music_song + " END END" + 199a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1, 200a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, 201a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY, 20263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath "(CASE WHEN grouporder=1 THEN '%1'" + // %1 gets replaced with localized string. 20363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" + 204e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" + 20563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2, 206a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen SearchManager.SUGGEST_COLUMN_INTENT_DATA 207a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 20863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath // Position of the TEXT_2 item in the above array. 20963f748ff8b258d9110038778a006b3000164fbeeSatish Sampath private final int SEARCH_COLUMN_BASIC_TEXT2 = 5; 210a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 211a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen private static final String[] sMediaTableColumns = new String[] { 21216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood FileColumns._ID, 213afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood FileColumns.MEDIA_TYPE, 2141717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood }; 2151717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 2167f36494e085c26c69cd5925e54028822025eff29Marco Nelissen private static final String[] sIdOnlyColumn = new String[] { 2177f36494e085c26c69cd5925e54028822025eff29Marco Nelissen FileColumns._ID 2187f36494e085c26c69cd5925e54028822025eff29Marco Nelissen }; 2197f36494e085c26c69cd5925e54028822025eff29Marco Nelissen 220166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen private static final String[] sDataOnlyColumn = new String[] { 221166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen FileColumns.DATA 222166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen }; 223166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen 224a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen private static final String[] sMediaTypeDataId = new String[] { 225a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen FileColumns.MEDIA_TYPE, 226a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen FileColumns.DATA, 227a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen FileColumns._ID 2284eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen }; 2294eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen 2304eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen private static final String[] sPlaylistIdPlayOrder = new String[] { 2314eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Playlists.Members.PLAYLIST_ID, 2324eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Playlists.Members.PLAY_ORDER 2334eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen }; 234a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen 235a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart"); 236a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen 23701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen private static final String CANONICAL = "canonical"; 23801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() { 240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onReceive(Context context, Intent intent) { 242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) { 2431f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood StorageVolume storage = (StorageVolume)intent.getParcelableExtra( 2441f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood StorageVolume.EXTRA_STORAGE_VOLUME); 2451f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // If primary external storage is ejected, then remove the external volume 2461f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // notify all cursors backed by data on that volume. 2471f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood if (storage.getPath().equals(mExternalStoragePaths[0])) { 2481f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood detachVolume(Uri.parse("content://media/external")); 2491f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood sFolderArtMap.clear(); 2501f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood MiniThumbFile.reset(); 2511f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } else { 2521f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // If secondary external storage is ejected, then we delete all database 2531f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // entries for that storage from the files table. 2541f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood synchronized (mDatabases) { 2551f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood DatabaseHelper database = mDatabases.get(EXTERNAL_VOLUME); 2561f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Uri uri = Uri.parse("file://" + storage.getPath()); 2571f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood if (database != null) { 2581f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood try { 2591f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // Send media scanner started and stopped broadcasts for apps that rely 2601f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // on these Intents for coarse grained media database notifications. 2611f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood context.sendBroadcast( 2621f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri)); 2631f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood 2641f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood // don't send objectRemoved events - MTP be sending StorageRemoved anyway 2651f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood mDisableMtpObjectCallbacks = true; 2661f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Log.d(TAG, "deleting all entries for storage " + storage); 2671f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood SQLiteDatabase db = database.getWritableDatabase(); 2684b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // First clear the file path to disable the _DELETE_FILE database hook. 2694b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // We do this to avoid deleting files if the volume is remounted while 2704b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // we are still processing the unmount event. 2714b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood ContentValues values = new ContentValues(); 27276611f01202d47396de5f5e9a3341ecc572900c4Marco Nelissen values.putNull(Files.FileColumns.DATA); 2734b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood String where = FileColumns.STORAGE_ID + "=?"; 2744b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) }; 2759b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumUpdates++; 2764b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood db.update("files", values, where, whereArgs); 2774b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // now delete the records 2789b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumDeletes++; 2799b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen int numpurged = db.delete("files", where, whereArgs); 2809b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen logToDb(db, "removed " + numpurged + 2819b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen " rows for ejected filesystem " + storage.getPath()); 2824b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood // notify on media Uris as well as the files Uri 2834b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood context.getContentResolver().notifyChange( 2844b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood Audio.Media.getContentUri(EXTERNAL_VOLUME), null); 2854b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood context.getContentResolver().notifyChange( 2864b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood Images.Media.getContentUri(EXTERNAL_VOLUME), null); 2874b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood context.getContentResolver().notifyChange( 2884b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood Video.Media.getContentUri(EXTERNAL_VOLUME), null); 2891f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood context.getContentResolver().notifyChange( 2901f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Files.getContentUri(EXTERNAL_VOLUME), null); 2911f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } catch (Exception e) { 2921f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood Log.e(TAG, "exception deleting storage entries", e); 2931f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } finally { 2941f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood context.sendBroadcast( 2951f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri)); 2961f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood mDisableMtpObjectCallbacks = false; 2971f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 2981f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 2991f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 3001f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood } 301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 305d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // set to disable sending events when the operation originates from MTP 306d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private boolean mDisableMtpObjectCallbacks; 307d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 308d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private final SQLiteDatabase.CustomFunction mObjectRemovedCallback = 309d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood new SQLiteDatabase.CustomFunction() { 310d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void callback(String[] args) { 3117f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // We could remove only the deleted entry from the cache, but that 3127f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // requires the path, which we don't have here, so instead we just 3137f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // clear the entire cache. 3147f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // TODO: include the path in the callback and only remove the affected 3157f36494e085c26c69cd5925e54028822025eff29Marco Nelissen // entry from the cache 3167f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.clear(); 317d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // do nothing if the operation originated from MTP 318d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (mDisableMtpObjectCallbacks) return; 319d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 320d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood Log.d(TAG, "object removed " + args[0]); 321d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood IMtpService mtpService = mMtpService; 322d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (mtpService != null) { 323d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood try { 324d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood sendObjectRemoved(Integer.parseInt(args[0])); 325d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } catch (NumberFormatException e) { 326d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e); 327d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 328d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 329d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 330d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood }; 331d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Wrapper class for a specific database (associated with one particular 334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * external card, or with internal storage). Can open the actual database 335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * on demand, create and upgrade the schema, etc. 336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 337fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn static final class DatabaseHelper extends SQLiteOpenHelper { 338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final Context mContext; 3395524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood final String mName; 340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final boolean mInternal; // True if this is the internal database 341fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn final boolean mEarlyUpgrade; 342fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn final SQLiteDatabase.CustomFunction mObjectRemovedCallback; 3435524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood boolean mUpgradeAttempted; // Used for upgrade error handling 34410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumQueries; 34510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumUpdates; 34610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumInserts; 34710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int mNumDeletes; 34810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long mScanStartTime; 34910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long mScanStopTime; 350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // In memory caches of artist and album data. 352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> mArtistCache = new HashMap<String, Long>(); 353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> mAlbumCache = new HashMap<String, Long>(); 354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 355fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn public DatabaseHelper(Context context, String name, boolean internal, 356fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn boolean earlyUpgrade, 357fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn SQLiteDatabase.CustomFunction objectRemovedCallback) { 35890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen super(context, name, null, getDatabaseVersion(context)); 359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext = context; 3605524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mName = name; 361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mInternal = internal; 362fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn mEarlyUpgrade = earlyUpgrade; 363fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn mObjectRemovedCallback = objectRemovedCallback; 3640e2a2386b39972286df21f4db5a9dd1df548c34dJeff Brown setWriteAheadLoggingEnabled(true); 365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Creates database the first time we try to open it. 369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onCreate(final SQLiteDatabase db) { 37290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen updateDatabase(mContext, db, mInternal, 0, getDatabaseVersion(mContext)); 373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Updates the database format when a new content provider is used 377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * with an older database format. 378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) { 3815524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mUpgradeAttempted = true; 38290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen updateDatabase(mContext, db, mInternal, oldV, newV); 383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 385db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin @Override 386db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin public synchronized SQLiteDatabase getWritableDatabase() { 3875524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood SQLiteDatabase result = null; 3885524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mUpgradeAttempted = false; 3895524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood try { 3905524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood result = super.getWritableDatabase(); 3915524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } catch (Exception e) { 3925524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood if (!mUpgradeAttempted) { 3935524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood Log.e(TAG, "failed to open database " + mName, e); 3945524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood return null; 3955524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 3965524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 3975524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood 3985524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // If we failed to open the database during an upgrade, delete the file and try again. 3995524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // This will result in the creation of a fresh database, which will be repopulated 4005524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // when the media scanner runs. 4015524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood if (result == null && mUpgradeAttempted) { 402450d884f1bd5de323a645ce1acfae40fb91b8cb0jangwon.lee mContext.deleteDatabase(mName); 4035524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood result = super.getWritableDatabase(); 4045524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 4055524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood return result; 4065524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 4075524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood 408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 409993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * For devices that have removable storage, we support keeping multiple databases 410993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * to allow users to switch between a number of cards. 411993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * On such devices, touch this particular database and garbage collect old databases. 412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * An LRU cache system is used to clean up databases for old external 413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * storage volumes. 414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onOpen(SQLiteDatabase db) { 41736d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky 418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mInternal) return; // The internal database is kept separately. 419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 420fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (mEarlyUpgrade) return; // Doing early upgrade. 421fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn 422fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (mObjectRemovedCallback != null) { 423fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback); 424fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 425d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 426993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // the code below is only needed on devices with removable storage 427993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (!Environment.isExternalStorageRemovable()) return; 428993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood 429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // touch the database file to show it is most recently used 430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(db.getPath()); 431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long now = System.currentTimeMillis(); 432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file.setLastModified(now); 433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used databases if we are over the limit 435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] databases = mContext.databaseList(); 436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count = databases.length; 437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int limit = MAX_EXTERNAL_DATABASES; 438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete external databases that have not been used in the past two months 440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long twoMonthsAgo = now - OBSOLETE_DATABASE_DB; 441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < databases.length; i++) { 442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File other = mContext.getDatabasePath(databases[i]); 443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) { 444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[i] = null; 445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (file.equals(other)) { 447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // reduce limit to account for the existence of the database we 448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // are about to open, which we removed from the list. 449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project limit--; 450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long time = other.lastModified(); 453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (time < twoMonthsAgo) { 454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]); 455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext.deleteDatabase(databases[i]); 456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[i] = null; 457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used databases until 463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we are no longer over the limit 464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (count > limit) { 465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int lruIndex = -1; 466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long lruTime = 0; 467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < databases.length; i++) { 469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (databases[i] != null) { 470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long time = mContext.getDatabasePath(databases[i]).lastModified(); 471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lruTime == 0 || time < lruTime) { 472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project lruIndex = i; 473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project lruTime = time; 474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used database 479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lruIndex != -1) { 480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]); 481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext.deleteDatabase(databases[lruIndex]); 482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[lruIndex] = null; 483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 48934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // synchronize on mMtpServiceConnection when accessing mMtpService 490d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private IMtpService mMtpService; 491d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 492d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private final ServiceConnection mMtpServiceConnection = new ServiceConnection() { 493d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void onServiceConnected(ComponentName className, android.os.IBinder service) { 49434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (this) { 49534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = IMtpService.Stub.asInterface(service); 49634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 497d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 498d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 499d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void onServiceDisconnected(ComponentName className) { 50034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (this) { 50134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 50234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 503d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 504d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood }; 505d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 506ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood private static final String[] sDefaultFolderNames = { 507ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_MUSIC, 508ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_PODCASTS, 509ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_RINGTONES, 510ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_ALARMS, 511ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_NOTIFICATIONS, 512ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_PICTURES, 513ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_MOVIES, 514ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_DOWNLOADS, 515ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_DCIM, 516ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood }; 517ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 518ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // creates default folders (Music, Downloads, etc) 51910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) { 520ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // Use a SharedPreference to ensure we only do this once. 521ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // We don't want to annoy the user by recreating the directories 522ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // after she has deleted them. 523ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); 524ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood if (prefs.getInt("created_default_folders", 0) == 0) { 525ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood for (String folderName : sDefaultFolderNames) { 526ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood File file = Environment.getExternalStoragePublicDirectory(folderName); 527ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood if (!file.exists()) { 528ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood file.mkdirs(); 52910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen insertDirectory(helper, db, file.getAbsolutePath()); 530ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 531ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 532ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 533ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood SharedPreferences.Editor e = prefs.edit(); 534ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.clear(); 535ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.putInt("created_default_folders", 1); 536ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.commit(); 537ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 538ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 539ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 5409311c7ff9d35ca3acc908da3da7a79fbf7a8da6bMarco Nelissen public static int getDatabaseVersion(Context context) { 54190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen try { 54290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen return context.getPackageManager().getPackageInfo( 54390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen context.getPackageName(), 0).versionCode; 54490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } catch (NameNotFoundException e) { 54590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen throw new RuntimeException("couldn't get version code for " + context); 54690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } 54790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } 54890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen 549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public boolean onCreate() { 551d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood final Context context = getContext(); 552d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 5535d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); 5545d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey 555acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " + 556acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums._ID); 557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album"); 558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key"); 559acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " + 560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.FIRST_YEAR); 561acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " + 562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.LAST_YEAR); 563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist"); 564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist"); 565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key"); 566acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " + 567acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums.NUMBER_OF_SONGS); 568acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " + 569acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums.ALBUM_ART); 570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 57163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] = 57263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll( 573d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood "%1", context.getString(R.string.artist_label)); 574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mDatabases = new HashMap<String, DatabaseHelper>(); 575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project attachVolume(INTERNAL_VOLUME); 576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT); 578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project iFilter.addDataScheme("file"); 579d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood context.registerReceiver(mUnmountReceiver, iFilter); 580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 581c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood StorageManager storageManager = 582c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood (StorageManager)context.getSystemService(Context.STORAGE_SERVICE); 583c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood mExternalStoragePaths = storageManager.getVolumePaths(); 5849be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood 585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // open external database if external storage is mounted 586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String state = Environment.getExternalStorageState(); 587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (Environment.MEDIA_MOUNTED.equals(state) || 588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { 589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project attachVolume(EXTERNAL_VOLUME); 590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 592ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND); 593ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang ht.start(); 594ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang mThumbHandler = new Handler(ht.getLooper()) { 595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void handleMessage(Message msg) { 597b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (msg.what == IMAGE_THUMB) { 598b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mMediaThumbQueue) { 59920434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest = mMediaThumbQueue.poll(); 600b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 60120434e032e498b716f87cce2f23dd646819218bfRay Chen if (mCurrentThumbRequest == null) { 602b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Log.w(TAG, "Have message but no request?"); 603b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } else { 604b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen try { 60520434e032e498b716f87cce2f23dd646819218bfRay Chen File origFile = new File(mCurrentThumbRequest.mPath); 6064d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen if (origFile.exists() && origFile.length() > 0) { 60720434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.execute(); 6084d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } else { 6094d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen // original file hasn't been stored yet 6104d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen synchronized (mMediaThumbQueue) { 61120434e032e498b716f87cce2f23dd646819218bfRay Chen Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath); 6124d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } 6134d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } 614b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } catch (IOException ex) { 6151d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen Log.w(TAG, ex); 6161d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen } catch (UnsupportedOperationException ex) { 6171d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen // This could happen if we unplug the sd card during insert/update/delete 6181d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen // See getDatabaseForUri. 6191d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen Log.w(TAG, ex); 62022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren } catch (OutOfMemoryError err) { 62122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren /* 62222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * Note: Catching Errors is in most cases considered 62322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * bad practice. However, in this case it is 62422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * motivated by the fact that corrupt or very large 62522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * images may cause a huge allocation to be 62622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * requested and denied. The bitmap handling API in 62722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * Android offers no other way to guard against 62822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * these problems than by catching OutOfMemoryError. 62922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren */ 63022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren Log.w(TAG, err); 631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } finally { 63220434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mCurrentThumbRequest) { 63320434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE; 63420434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.notifyAll(); 635b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 636b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 637b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 638b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } else if (msg.what == ALBUM_THUMB) { 639b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ThumbData d; 640b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mThumbRequestStack) { 641b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen d = (ThumbData)mThumbRequestStack.pop(); 642b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 6438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 644d63eb65db514ad7064951f221f0278a3f2293411Jeff Sharkey IoUtils.closeQuietly(makeThumbInternal(d)); 645b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mPendingThumbs) { 646b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen mPendingThumbs.remove(d.path); 647b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 6488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return true; 653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 655395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_FILES = "files"; 656395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_ALBUM_ART = "album_art"; 657395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_THUMBNAILS = "thumbnails"; 658395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey private static final String TABLE_VIDEO_THUMBNAILS = "videothumbnails"; 659395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 660afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String IMAGE_COLUMNS = 661afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_size,_display_name,mime_type,title,date_added," + 662afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified,description,picasa_id,isprivate,latitude,longitude," + 663bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," + 664bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "width,height"; 665bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 666bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang private static final String IMAGE_COLUMNSv407 = 667bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "_data,_size,_display_name,mime_type,title,date_added," + 668bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "date_modified,description,picasa_id,isprivate,latitude,longitude," + 669afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name"; 670afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 671805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen private static final String AUDIO_COLUMNSv99 = 672afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_display_name,_size,mime_type,date_added," + 673afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified,title,title_key,duration,artist_id,composer,album_id," + 674afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 675afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bookmark"; 676afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 677805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen private static final String AUDIO_COLUMNSv100 = 678805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "_data,_display_name,_size,mime_type,date_added," + 679805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "date_modified,title,title_key,duration,artist_id,composer,album_id," + 680805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 681805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "bookmark,album_artist"; 682805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen 683957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang private static final String AUDIO_COLUMNSv405 = 684957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "_data,_display_name,_size,mime_type,date_added,is_drm," + 685957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "date_modified,title,title_key,duration,artist_id,composer,album_id," + 686957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 687957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang "bookmark,album_artist"; 688957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 689afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String VIDEO_COLUMNS = 690afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_display_name,_size,mime_type,date_added,date_modified," + 691afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title,duration,artist,album,resolution,description,isprivate,tags," + 692afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "category,language,mini_thumb_data,latitude,longitude,datetaken," + 693bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," + 694bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "height"; 695bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 696bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang private static final String VIDEO_COLUMNSv407 = 697bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "_data,_display_name,_size,mime_type,date_added,date_modified," + 698bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "title,duration,artist,album,resolution,description,isprivate,tags," + 699bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang "category,language,mini_thumb_data,latitude,longitude,datetaken," + 700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_magic,bucket_id,bucket_display_name, bookmark"; 701afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 702afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified"; 703afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * This method takes care of updating all the tables in the database to the 706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * current version, creating them if necessary. 707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * This method can only update databases at schema 63 or higher, which was 708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * created August 1, 2008. Older database will be cleared and recreated. 709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db Database 710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param internal True if this is the internal media database 711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 71290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen private static void updateDatabase(Context context, SQLiteDatabase db, boolean internal, 713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int fromVersion, int toVersion) { 714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // sanity checks 71690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen int dbversion = getDatabaseVersion(context); 71790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen if (toVersion != dbversion) { 71890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + dbversion); 719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException(); 720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (fromVersion > toVersion) { 72195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion + 722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " to " + toVersion + ". Did you forget to wipe data?"); 723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException(); 724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 725988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen long startTime = SystemClock.currentTimeMicro(); 726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 727d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag. 728acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // We can't downgrade from those revisions, so start over. 729022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen // (the initial change to do this was wrong, so now we actually need to start over 730022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen // if the database version is 84-89) 731bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair. 732bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // However version 91 was reused in a divergent development path for gingerbread, 733bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // so we need to support upgrades from 91. 734bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Therefore we will only force a reset for versions 92 - 94. 735d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) || 736bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin (fromVersion >= 92 && fromVersion <= 94)) { 737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Drop everything and start over. 738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.i(TAG, "Upgrading media database from version " + 739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fromVersion + " to " + toVersion + ", which will destroy all old data"); 740d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen fromVersion = 63; 741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS images"); 742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS images_cleanup"); 743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS thumbnails"); 744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup"); 745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_meta"); 746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS artists"); 747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS albums"); 748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS album_art"); 749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS artist_info"); 750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS album_info"); 751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS artists_albums_map"); 752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_genres"); 754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_genres_map"); 755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup"); 756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_playlists"); 757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_playlists_map"); 758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup"); 759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1"); 760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2"); 761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS video"); 762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS video_cleanup"); 763cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood db.execSQL("DROP TABLE IF EXISTS objects"); 7649ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup"); 7659ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup"); 7669ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup"); 7679ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup"); 768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS images (" + 770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + 772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT," + 776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "description TEXT," + 779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "picasa_id TEXT," + 780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "isprivate INTEGER," + 781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "latitude DOUBLE," + 782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "longitude DOUBLE," + 783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "datetaken INTEGER," + 784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "orientation INTEGER," + 785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_magic INTEGER," + 786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "bucket_id TEXT," + 787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "bucket_display_name TEXT" + 788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);"); 791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " + 793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM thumbnails WHERE image_id = old._id;" + 795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 798b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // create image thumbnail table 799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" + 800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + 802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "image_id INTEGER," + 803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "kind INTEGER," + 804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "width INTEGER," + 805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "height INTEGER" + 806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);"); 809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " + 811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains meta data about audio files 816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" + 817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 818216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen "_data TEXT UNIQUE NOT NULL," + 819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT NOT NULL," + 825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title_key TEXT NOT NULL," + 826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "duration INTEGER," + 827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_id INTEGER," + 828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "composer TEXT," + 829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER," + 830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "track INTEGER," + // track is an integer to allow proper sorting 831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "year INTEGER CHECK(year!=0)," + 832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_ringtone INTEGER," + 833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_music INTEGER," + 834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_alarm INTEGER," + 835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_notification INTEGER" + 836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains a sort/group "key" and the preferred display name for artists 839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS artists (" + 840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_id INTEGER PRIMARY KEY," + 841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_key TEXT NOT NULL UNIQUE," + 842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist TEXT NOT NULL" + 843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains a sort/group "key" and the preferred display name for albums 846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS albums (" + 847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER PRIMARY KEY," + 848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_key TEXT NOT NULL UNIQUE," + 849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album TEXT NOT NULL" + 850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" + 853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER PRIMARY KEY," + 854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT" + 855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project recreateAudioView(db); 85895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides some extra info about artists, like the number of tracks 861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // and albums for this artist 862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " + 863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT artist_id AS _id, artist, artist_key, " + 864acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "COUNT(DISTINCT album) AS number_of_albums, " + 865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+ 866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "GROUP BY artist_key;"); 867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides extra info albums, such as the number of tracks 869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " + 870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT audio.album_id AS _id, album, album_key, " + 871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "MIN(year) AS minyear, " + 872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "MAX(year) AS maxyear, artist, artist_id, artist_key, " + 873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS + 874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ",album_art._data AS album_art" + 875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" + 876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " WHERE is_music=1 GROUP BY audio.album_id;"); 877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 878acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // For a given artist_id, provides the album_id for albums on 879acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // which the artist appears. 880acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " + 881acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "SELECT DISTINCT artist_id, album_id FROM audio_meta;"); 882acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /* 884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Only external media volumes can handle genres, playlists, etc. 885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!internal) { 887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio file is deleted 888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " + 889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_genres_map WHERE audio_id = old._id;" + 891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" + 892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains audio genre definitions 895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" + 896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "name TEXT NOT NULL" + 898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 90078b2885edc406273d688536b0eadfea006b20662Marco Nelissen // Contains mappings between audio genres and audio files 901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" + 902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_id INTEGER NOT NULL," + 904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "genre_id INTEGER NOT NULL" + 905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio genre is delete 908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " + 909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_genres_map WHERE genre_id = old._id;" + 911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains audio playlist definitions 914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" + 915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + // _data is path for file based playlists, or null 917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "name TEXT NOT NULL," + 918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER" + 920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains mappings between audio playlists and audio files 923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" + 924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_id INTEGER NOT NULL," + 926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "playlist_id INTEGER NOT NULL," + 927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "play_order INTEGER NOT NULL" + 928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio playlist is deleted 931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " + 932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up album_art table entry when an album is deleted 938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " + 939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM album_art WHERE album_id = old.album_id;" + 941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up album_art when an album is deleted 944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " + 945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains meta data about video files 951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS video (" + 952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT NOT NULL," + 954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT," + 960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "duration INTEGER," + 961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist TEXT," + 962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album TEXT," + 963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "resolution TEXT," + 964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "description TEXT," + 965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "isprivate INTEGER," + // for YouTube videos 966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "tags TEXT," + // for YouTube videos 967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "category TEXT," + // for YouTube videos 968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "language TEXT," + // for YouTube videos 969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_data TEXT," + 970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "latitude DOUBLE," + 971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "longitude DOUBLE," + 972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "datetaken INTEGER," + 973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_magic INTEGER" + 974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " + 977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // At this point the database is at least at schema version 63 (it was 983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // either created at version 63 by the code above, or was already at 984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // version 63 or later) 985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 64) { 987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the index that updates the database to schema version 64 988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);"); 989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 991acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 992acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.0 shipped with database version 64 993acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 994acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 65) { 996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the index that updates the database to schema version 65 997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);"); 998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1000403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // In version 66, originally we updateBucketNames(db, "images"), 1001403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // but we need to do it in version 89 and therefore save the update here. 1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 67) { 1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the indices that update the database to schema version 67 1005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);"); 1006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);"); 1007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 68) { 1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create bucket_id and bucket_display_name columns for the video table. 1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;"); 1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT"); 1013403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen 1014403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // In version 68, originally we updateBucketNames(db, "video"), 1015403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // but we need to do it in version 89 and therefore save the update here. 1016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 69) { 1019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project updateDisplayName(db, "images"); 1020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 70) { 1023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create bookmark column for the video table. 1024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;"); 1025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 102695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 71) { 1028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // There is no change to the database schema, however a code change 1029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // fixed parsing of metadata for certain files bought from the 1030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // iTunes music store, so we want to rescan files that might need it. 1031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // We do this by clearing the modification date in the database for 1032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // those files, so that the media scanner will see them as updated 1033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // and rescan them. 1034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" + 1035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _id FROM audio where mime_type='audio/mp4' AND " + 1036e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "artist='" + MediaStore.UNKNOWN_STRING + "' AND " + 1037e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "album='" + MediaStore.UNKNOWN_STRING + "'" + 1038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 1039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 104095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 72) { 1042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create is_podcast and bookmark columns for the audio table. 1043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;"); 1044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';"); 1045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" + 1046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND _data NOT LIKE '%/music/%';"); 1047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;"); 1048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // New columns added to tables aren't visible in views on those tables 1050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // without opening and closing the database (or using the 'vacuum' command, 1051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // which we can't do here because all this code runs inside a transaction). 1052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // To work around this, we drop and recreate the affected view and trigger. 1053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project recreateAudioView(db); 1054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 105595ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1056acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1057acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.5 shipped with database version 72 1058acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1059acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 10608d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen if (fromVersion < 73) { 10618d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // There is no change to the database schema, but we now do case insensitive 10628d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // matching of folder names when determining whether something is music, a 10638d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // ringtone, podcast, etc, so we might need to reclassify some files. 10648d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " + 10658d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/music/%';"); 10668d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " + 10678d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/ringtones/%';"); 10688d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " + 10698d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/notifications/%';"); 10708d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " + 10718d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/alarms/%';"); 10728d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " + 10738d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/podcasts/%';"); 10748d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen } 1075a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 1076a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (fromVersion < 74) { 1077a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // This view is used instead of the audio view by the union below, to force 1078a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // sqlite to use the title_key index. This greatly reduces memory usage 1079a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // (no separate copy pass needed for sorting, which could cause errors on 1080a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // large datasets) and improves speed (by about 35% on a large dataset) 1081a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " + 1082a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "ORDER BY title_key;"); 1083a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 1084a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS search AS " + 1085a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT _id," + 1086a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'artist' AS mime_type," + 1087a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 1088a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS album," + 1089a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS title," + 1090a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text1," + 1091a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS text2," + 1092a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "number_of_albums AS data1," + 1093a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "number_of_tracks AS data2," + 1094a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key AS match," + 1095a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/artists/'||_id AS suggest_intent_data," + 1096a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "1 AS grouporder " + 1097e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " + 1098a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "UNION ALL " + 1099a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT _id," + 1100a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'album' AS mime_type," + 1101a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 1102a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album," + 1103a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS title," + 1104a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album AS text1," + 1105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text2," + 1106a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data1," + 1107a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data2," + 1108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key||' '||album_key AS match," + 1109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/albums/'||_id AS suggest_intent_data," + 1110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "2 AS grouporder " + 1111e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " + 1112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "UNION ALL " + 1113a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT searchhelpertitle._id AS _id," + 1114a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "mime_type," + 1115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 1116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album," + 1117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "title," + 1118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "title AS text1," + 1119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text2," + 1120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data1," + 1121a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data2," + 1122a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key||' '||album_key||' '||title_key AS match," + 1123a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/media/'||searchhelpertitle._id AS " + 1124a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "suggest_intent_data," + 1125a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "3 AS grouporder " + 1126a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "FROM searchhelpertitle WHERE (title != '') " 1127a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ); 1128a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } 112959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 113059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (fromVersion < 75) { 113195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen // Force a rescan of the audio entries so we can apply the new logic to 113259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // distinguish same-named albums. 113359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen db.execSQL("UPDATE audio_meta SET date_modified=0;"); 113459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen db.execSQL("DELETE FROM albums"); 113559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 113615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen 113715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen if (fromVersion < 76) { 113815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen // We now ignore double quotes when building the key, so we have to remove all of them 113915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen // from existing keys. 114015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE audio_meta SET title_key=" + 114115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(title_key,x'081D08C29F081D',x'081D') " + 114215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';"); 114315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE albums SET album_key=" + 114415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(album_key,x'081D08C29F081D',x'081D') " + 114515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';"); 114615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE artists SET artist_key=" + 114715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(artist_key,x'081D08C29F081D',x'081D') " + 114815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';"); 114915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen } 1150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1151acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1152acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.6 shipped with database version 76 1153acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1154acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 1155b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (fromVersion < 77) { 1156b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // create video thumbnail table 1157b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" + 1158b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "_id INTEGER PRIMARY KEY," + 1159b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "_data TEXT," + 1160b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "video_id INTEGER," + 1161b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "kind INTEGER," + 1162b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "width INTEGER," + 1163b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "height INTEGER" + 1164b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ");"); 1165b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1166b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);"); 1167b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1168b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " + 1169b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "BEGIN " + 1170b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "SELECT _DELETE_FILE(old._data);" + 1171b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "END"); 1172b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 11731769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen 1174acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1175acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 2.0 and 2.0.1 shipped with database version 77 1176acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1177acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 11781769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen if (fromVersion < 78) { 1179044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen // Force a rescan of the video entries so we can update 11801769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen // latest changed DATE_TAKEN units (in milliseconds). 11811769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen db.execSQL("UPDATE video SET date_modified=0;"); 11821769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen } 1183268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen 1184acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1185acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 2.1 shipped with database version 78 1186acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1187acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 1188268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if (fromVersion < 79) { 1189268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // move /sdcard/albumthumbs to 1190268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // /sdcard/Android/data/com.android.providers.media/albumthumbs, 1191268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // and update the database accordingly 1192268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen 11939be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs"; 11949be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER; 1195268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen File thumbsfolder = new File(oldthumbspath); 1196268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if (thumbsfolder.exists()) { 1197268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // move folder to its new location 1198268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen File newthumbsfolder = new File(newthumbspath); 1199268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen newthumbsfolder.getParentFile().mkdirs(); 1200268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if(thumbsfolder.renameTo(newthumbsfolder)) { 1201268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // update the database 1202268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" + 1203268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen oldthumbspath + "','" + newthumbspath + "');"); 1204268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1205268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1206268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1207044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen 1208044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen if (fromVersion < 80) { 1209044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen // Force rescan of image entries to update DATE_TAKEN as UTC timestamp. 1210044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen db.execSQL("UPDATE images SET date_modified=0;"); 1211044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen } 12120ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 1213166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen if (fromVersion < 81 && !internal) { 12140ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Delete entries starting with /mnt/sdcard. This is for the benefit 12150ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // of users running builds between 2.0.1 and 2.1 final only, since 12160ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // users updating from 2.0 or earlier will not have such entries. 12170ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 12180ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // First we need to update the _data fields in the affected tables, since 12190ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // otherwise deleting the entries will also delete the underlying files 12200ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // (via a trigger), and we want to keep them. 12210ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12220ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12230ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12240ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12250ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12260ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 1227216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 12280ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Once the paths have been renamed, we can safely delete the entries 12290ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';"); 12300ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM images WHERE _data IS '////';"); 12310ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM video WHERE _data IS '////';"); 12320ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';"); 12330ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';"); 12340ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM audio_meta WHERE _data IS '////';"); 12350ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM album_art WHERE _data IS '////';"); 12360ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 12370ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // rename existing entries starting with /sdcard to /mnt/sdcard 12380ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_meta" + 12390ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12400ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_playlists" + 12410ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12420ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE images" + 12430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12440ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE video" + 12450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE videothumbnails" + 12470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE thumbnails" + 12490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE album_art" + 12510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 12520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 12530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Delete albums and artists, then clear the modification time on songs, which 12540ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // will cause the media scanner to rescan everything, rebuilding the artist and 12550ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // album tables along the way, while preserving playlists. 12560ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // We need this rescan because ICU also changed, and now generates different 12570ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // collation keys 12580ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE from albums"); 12590ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE from artists"); 12600ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_meta SET date_modified=0;"); 12610ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen } 126284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen 126384403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen if (fromVersion < 82) { 1264acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // recreate this view with the correct "group by" specifier 126584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen db.execSQL("DROP VIEW IF EXISTS artist_info"); 126684403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " + 126784403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "SELECT artist_id AS _id, artist, artist_key, " + 1268acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "COUNT(DISTINCT album_key) AS number_of_albums, " + 126984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+ 127084403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "GROUP BY artist_key;"); 127184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen } 1272216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen 1273acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* we skipped over version 83, and reverted versions 84, 85 and 86 */ 1274ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen 1275ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen if (fromVersion < 87) { 1276ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // The fastscroll thumb needs an index on the strings being displayed, 1277ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // otherwise the queries it does to determine the correct position 1278ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // becomes really inefficient 1279022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);"); 1280022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);"); 1281022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);"); 1282ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen } 1283216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen 1284acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen if (fromVersion < 88) { 1285acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // Clean up a few more things from versions 84/85/86, and recreate 1286acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // the few things worth keeping from those changes. 1287acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update1;"); 1288acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update2;"); 1289acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update3;"); 1290acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update4;"); 1291acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update1;"); 1292acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update2;"); 1293acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update3;"); 1294acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update4;"); 129516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP VIEW IF EXISTS album_artists;"); 1296acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);"); 1297acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);"); 1298acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // For a given artist_id, provides the album_id for albums on 1299acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // which the artist appears. 1300acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " + 1301acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "SELECT DISTINCT artist_id, album_id FROM audio_meta;"); 1302acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen } 1303403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen 1304fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // In version 89, originally we updateBucketNames(db, "images") and 1305fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames 1306fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // for all files and therefore can save the update here. 1307b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 1308b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (fromVersion < 91) { 1309bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Never query by mini_thumb_magic_index 1310bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index"); 1311bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin 1312bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // sort the items by taken date in each bucket 1313bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)"); 1314bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)"); 1315bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin } 1316bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin 1317a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu 1318d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // Gingerbread ended up going to version 100, but didn't yet have the "files" 1319d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // table, so we need to create that if we're at 100 or lower. This means 1320d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // we won't be able to upgrade pre-release Honeycomb. 1321d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen if (fromVersion <= 100) { 1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Remove various stages of work in progress for MTP support 1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS objects"); 1324afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS files"); 132516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;"); 132616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;"); 132716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;"); 132816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;"); 1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;"); 1330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;"); 1331afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;"); 1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;"); 1333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;"); 1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Create a new table to manage all files in our storage. 1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // This contains a union of all the columns from the old 1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // images, audio_meta, videos and audio_playlist tables. 1338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TABLE files (" + 133900a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 1340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data TEXT," + // this can be null for playlists 1341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_size INTEGER," + 1342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "format INTEGER," + 1343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "parent INTEGER," + 1344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_added INTEGER," + 1345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified INTEGER," + 1346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mime_type TEXT," + 1347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title TEXT," + 1348afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "description TEXT," + 1349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_display_name TEXT," + 1350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for images 1352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "picasa_id TEXT," + 1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "orientation INTEGER," + 1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for images and video 1356afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "latitude DOUBLE," + 1357afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "longitude DOUBLE," + 1358afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "datetaken INTEGER," + 1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_magic INTEGER," + 1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bucket_id TEXT," + 1361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bucket_display_name TEXT," + 1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "isprivate INTEGER," + 1363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for audio 1365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title_key TEXT," + 1366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "artist_id INTEGER," + 1367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "album_id INTEGER," + 1368afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "composer TEXT," + 1369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "track INTEGER," + 1370afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "year INTEGER CHECK(year!=0)," + 1371afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_ringtone INTEGER," + 1372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_music INTEGER," + 1373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_alarm INTEGER," + 1374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_notification INTEGER," + 1375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_podcast INTEGER," + 1376d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen "album_artist TEXT," + 1377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1378afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for audio and video 1379afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "duration INTEGER," + 1380afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bookmark INTEGER," + 1381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for video 1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "artist TEXT," + 1384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "album TEXT," + 1385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "resolution TEXT," + 1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "tags TEXT," + 1387afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "category TEXT," + 1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "language TEXT," + 1389afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_data TEXT," + 1390afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1391afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for playlists 1392afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "name TEXT," + 1393afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1394afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // media_type is used by the views to emulate the old 1395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // images, audio_meta, videos and audio_playlist tables. 1396afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "media_type INTEGER," + 1397afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1398afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Value of _id from the old media table. 1399afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Used only for updating other tables during database upgrade. 1400afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "old_id INTEGER" + 1401afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood ");"); 1402d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen 1403afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX path_index ON files(_data);"); 1404afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX media_type_index ON files(media_type);"); 1405afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1406afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Copy all data from our obsolete tables to the new files table 140792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood 140892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // Copy audio records first, preserving the _id column. 140992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // We do this to maintain compatibility for content Uris for ringtones. 141092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // Unfortunately we cannot do this for images and videos as well. 141192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // We choose to do this for the audio table because the fragility of Uris 141292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood // for ringtones are the most common problem we need to avoid. 141392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" + 141492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO + 141592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood " FROM audio_meta;"); 141692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood 1417bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT " 1418bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;"); 1419bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT " 1420bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;"); 142116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood if (!internal) { 1422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT " 1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST 1424afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " FROM audio_playlists;"); 1425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 142616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood 1427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Delete the old tables 1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS images"); 1429afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS audio_meta"); 1430afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS video"); 1431afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS audio_playlists"); 143216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood 1433afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Create views to replace our old tables 1434bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 + 1435afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_IMAGE + ";"); 1437d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 + 1438d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1439d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1440bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 + 1441afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1442afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_VIDEO + ";"); 1443afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1444afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS + 1445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_PLAYLIST + ";"); 144716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood } 144836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 14499491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen // create temporary index to make the updates go faster 14509491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen db.execSQL("CREATE INDEX tmp ON files(old_id);"); 14519491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen 1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update the image_id column in the thumbnails table. 1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files " 1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = thumbnails.image_id AND files.media_type = " 1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_IMAGE + ");"); 145636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1458d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // update audio_id in the audio_genres_map table, and 1459d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen // audio_playlists_map tables and playlist_id in the audio_playlists_map table 1460afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files " 1461afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = " 1462afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_AUDIO + ");"); 1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files " 1464afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = audio_playlists_map.audio_id " 1465afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");"); 1466d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files " 1467d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen + "WHERE files.old_id = audio_playlists_map.playlist_id " 1468d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");"); 1469afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 1470afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1471afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update video_id in the videothumbnails table. 1472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files " 1473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = " 1474afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_VIDEO + ");"); 1475afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 14769491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen // we don't need this index anymore now 14779491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen db.execSQL("DROP INDEX tmp;"); 14789491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen 1479afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update indices to work on the files table 1480afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS title_idx"); 1481afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS album_id_idx"); 1482afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS image_bucket_index"); 1483afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS video_bucket_index"); 1484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS sort_index"); 1485afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS titlekey_index"); 1486afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS artist_id_idx"); 1487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX title_idx ON files(title);"); 1488afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX album_id_idx ON files(album_id);"); 1489afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);"); 1490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);"); 1491afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX titlekey_index ON files(title_key);"); 1492afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);"); 1493afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1494afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Recreate triggers for our obsolete tables on the new files table 1495afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_cleanup"); 1496afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 1497afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_cleanup"); 1498afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup"); 1499afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_delete"); 150036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1501afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " + 1502afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " + 150336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1504afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM thumbnails WHERE image_id = old._id;" + 1505afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 150636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 150736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1508afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " + 150949dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " + 151036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 151236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 151336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1514afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1515afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " + 1516afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " + 1517afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "BEGIN " + 1518afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_genres_map WHERE audio_id = old._id;" + 1519afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" + 1520afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "END"); 1521afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1522afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " + 1523afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " + 1524afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "BEGIN " + 1525afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 1526afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 1527afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "END"); 1528afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1529afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " + 153036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1531afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from files where _id=old._id;" + 1532afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from audio_playlists_map where audio_id=old._id;" + 1533afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from audio_genres_map where audio_id=old._id;" + 153436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 153536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood } 153636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood } 153736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1538db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin if (fromVersion < 301) { 1539db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("DROP INDEX IF EXISTS bucket_index"); 1540db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)"); 1541db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)"); 1542db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin } 1543db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin 154420405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood if (fromVersion < 302) { 154520405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood db.execSQL("CREATE INDEX parent_index ON files(parent);"); 154620405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood db.execSQL("CREATE INDEX format_index ON files(format);"); 154720405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood } 154820405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood 15492658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (fromVersion < 303) { 15502658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // the album disambiguator hash changed, so rescan songs and force 15512658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // albums to be updated. Artists are unaffected. 15522658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen db.execSQL("DELETE from albums"); 15532658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 15542658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 15552658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } 15562658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 15574b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood if (fromVersion < 304 && !internal) { 155851d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood // notifies host when files are deleted 155951d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " + 156051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "BEGIN " + 156151d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "SELECT _OBJECT_REMOVED(old._id);" + 156251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "END"); 156351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood 156451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood } 156551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood 15664b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood if (fromVersion < 305 && internal) { 15674b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood // version 304 erroneously added this trigger to the internal database 15684b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup"); 15694b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood } 15704b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood 1571fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen if (fromVersion < 306 && !internal) { 1572efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen // The genre list was expanded and genre string parsing was tweaked, so 1573efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen // rebuild the genre list 1574efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 1575efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1576efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("DELETE FROM audio_genres_map"); 1577efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("DELETE FROM audio_genres"); 1578efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen } 1579efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen 158065587f9c204abb2d179527dfdca009f4780e9743Ray Chen if (fromVersion < 307 && !internal) { 158165587f9c204abb2d179527dfdca009f4780e9743Ray Chen // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or 158265587f9c204abb2d179527dfdca009f4780e9743Ray Chen // EXIF local time. 158365587f9c204abb2d179527dfdca009f4780e9743Ray Chen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 158465587f9c204abb2d179527dfdca009f4780e9743Ray Chen + FileColumns.MEDIA_TYPE_IMAGE + ";"); 158565587f9c204abb2d179527dfdca009f4780e9743Ray Chen } 158665587f9c204abb2d179527dfdca009f4780e9743Ray Chen 1587e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // Honeycomb went up to version 307, ICS started at 401 1588e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 15894f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood // Database version 401 did not add storage_id to the internal database. 15904f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood // We need it there too, so add it in version 402 15914f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood if (fromVersion < 401 || (fromVersion == 401 && internal)) { 15929be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood // Add column for MTP storage ID 15939be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;"); 15949be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood // Anything in the database before this upgrade step will be in the primary storage 15959be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";"); 15969be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 15979be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood 159878b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (fromVersion < 403 && !internal) { 159978b2885edc406273d688536b0eadfea006b20662Marco Nelissen db.execSQL("CREATE VIEW audio_genres_map_noid AS " + 160078b2885edc406273d688536b0eadfea006b20662Marco Nelissen "SELECT audio_id,genre_id from audio_genres_map;"); 160178b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 160278b2885edc406273d688536b0eadfea006b20662Marco Nelissen 16039289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (fromVersion < 404) { 16049289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen // There was a bug that could cause distinct same-named albums to be 16059289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen // combined again. Delete albums and force a rescan. 16069289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen db.execSQL("DELETE from albums"); 16079289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 16089289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 16099289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 16109289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen 1611957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang if (fromVersion < 405) { 1612957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang // Add is_drm column. 1613957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;"); 1614957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 1615957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang db.execSQL("DROP VIEW IF EXISTS audio_meta"); 1616957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 + 1617957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1618957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1619957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 1620957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang recreateAudioView(db); 1621957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang } 1622957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang 1623b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (fromVersion < 407) { 16247ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang // Rescan files in the media database because a new column has been added 1625b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // in table files in version 405 and to recover from problems populating 1626b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // the genre tables 16277ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang db.execSQL("UPDATE files SET date_modified=0;"); 16287ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang } 16297ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang 1630bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang if (fromVersion < 408) { 1631bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang // Add the width/height columns for images and video 1632bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;"); 1633bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;"); 1634bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 1635bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang // Rescan files to fill the columns 1636bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("UPDATE files SET date_modified=0;"); 1637bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 1638bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang // Update images and video views to contain the width/height columns 1639bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("DROP VIEW IF EXISTS images"); 1640bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("DROP VIEW IF EXISTS video"); 1641bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS + 1642bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1643bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + FileColumns.MEDIA_TYPE_IMAGE + ";"); 1644bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS + 1645bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1646bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang + FileColumns.MEDIA_TYPE_VIDEO + ";"); 1647bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang } 1648bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang 16495809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen if (fromVersion < 409 && !internal) { 16505809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen // A bug that prevented numeric genres from being parsed was fixed, so 16515809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen // rebuild the genre list 16525809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 16535809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 16545809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen db.execSQL("DELETE FROM audio_genres_map"); 16555809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen db.execSQL("DELETE FROM audio_genres"); 16565809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen } 16575809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen 1658e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // ICS went out with database version 409, JB started at 500 1659e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1660166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen if (fromVersion < 500) { 1661166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen // we're now deleting the file in mediaprovider code, rather than via a trigger 1662166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;"); 1663166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } 1664a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen if (fromVersion < 501) { 1665a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // we're now deleting the file in mediaprovider code, rather than via a trigger 1666a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // the images_cleanup trigger would delete the image file and the entry 1667a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // in the thumbnail table, which in turn would trigger thumbnails_cleanup 1668a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen // to delete the thumbnail image 1669a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS images_cleanup;"); 1670a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup;"); 1671a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 16725afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen if (fromVersion < 502) { 16735afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen // we're now deleting the file in mediaprovider code, rather than via a trigger 16745afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS video_cleanup;"); 16755afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen } 16765118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen if (fromVersion < 503) { 16775118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen // genre and playlist cleanup now done in mediaprovider code, instead of in a trigger 16785118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS audio_delete"); 16795118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 16805118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen } 168190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen if (fromVersion < 504) { 168290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen // add an index to help with case-insensitive matching of paths 168390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen db.execSQL( 168490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen "CREATE INDEX IF NOT EXISTS path_index_lower ON files(_data COLLATE NOCASE);"); 168590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen } 16867074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen if (fromVersion < 505) { 16877074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen // Starting with schema 505 we fill in the width/height/resolution columns for videos, 16887074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen // so force a rescan of videos to fill in the blanks 16897074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 16907074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen + FileColumns.MEDIA_TYPE_VIDEO + ";"); 16917074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen } 169259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen if (fromVersion < 506) { 169359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // sd card storage got moved to /storage/sdcard0 169459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // first delete everything that already got scanned in /storage before this 169559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // update step was added 169659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DROP TRIGGER IF EXISTS files_cleanup"); 169759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM files WHERE _data LIKE '/storage/%';"); 169859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM album_art WHERE _data LIKE '/storage/%';"); 169959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM thumbnails WHERE _data LIKE '/storage/%';"); 170059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("DELETE FROM videothumbnails WHERE _data LIKE '/storage/%';"); 170159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // then rename everything from /mnt/sdcard/ to /storage/sdcard0, 170259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen // and from /mnt/external1 to /storage/sdcard1 170359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE files SET " + 170459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 170559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE files SET " + 170659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 170759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE album_art SET " + 170859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 170959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE album_art SET " + 171059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 171159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE thumbnails SET " + 171259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 171359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE thumbnails SET " + 171459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 171559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE videothumbnails SET " + 171659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';"); 171759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("UPDATE videothumbnails SET " + 171859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';"); 171959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen 172059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen if (!internal) { 172159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " + 172259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "BEGIN " + 172359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "SELECT _OBJECT_REMOVED(old._id);" + 172459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen "END"); 172559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen } 172659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen } 1727a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin if (fromVersion < 507) { 1728a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin // we update _data in version 506, we need to update the bucket_id as well 1729677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen updateBucketNames(db); 1730a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin } 173146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen if (fromVersion < 508 && !internal) { 173246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen // ensure we don't get duplicate entries in the genre map 173346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map_tmp (" + 173446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "_id INTEGER PRIMARY KEY," + 173546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "audio_id INTEGER NOT NULL," + 173646e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "genre_id INTEGER NOT NULL," + 173746e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen "UNIQUE (audio_id,genre_id) ON CONFLICT IGNORE" + 173846e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen ");"); 173946e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("INSERT INTO audio_genres_map_tmp (audio_id,genre_id)" + 174046e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen " SELECT DISTINCT audio_id,genre_id FROM audio_genres_map;"); 174146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("DROP TABLE audio_genres_map;"); 174246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen db.execSQL("ALTER TABLE audio_genres_map_tmp RENAME TO audio_genres_map;"); 174346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen } 1744988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 1745988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (fromVersion < 509) { 1746988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen db.execSQL("CREATE TABLE IF NOT EXISTS log (time DATETIME PRIMARY KEY, message TEXT);"); 1747988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 1748395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 1749395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey // Emulated external storage moved to user-specific paths 1750395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey if (fromVersion < 510 && Environment.isExternalStorageEmulated()) { 1751395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey // File.fixSlashes() removes any trailing slashes 1752395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey final String externalStorage = Environment.getExternalStorageDirectory().toString(); 175357b65f10aaafc4bf42a0fa59eb2bbe6c8371c2e6Jeff Sharkey Log.d(TAG, "Adjusting external storage paths to: " + externalStorage); 1754395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 1755395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey final String[] tables = { 1756395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey TABLE_FILES, TABLE_ALBUM_ART, TABLE_THUMBNAILS, TABLE_VIDEO_THUMBNAILS }; 1757395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey for (String table : tables) { 1758395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey db.execSQL("UPDATE " + table + " SET " + "_data='" + externalStorage 1759395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey + "'||SUBSTR(_data,17) WHERE _data LIKE '/storage/sdcard0/%';"); 1760395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey } 1761395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey } 17628bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey if (fromVersion < 511) { 17638bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey // we update _data in version 510, we need to update the bucket_id as well 17648bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey updateBucketNames(db); 17658bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey } 1766395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey 1767e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // JB 4.2 went out with database version 511, starting next release with 600 1768e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1769e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen if (fromVersion < 600) { 1770e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // modify _data column to be unique and collate nocase. Because this drops the original 1771e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // table and replaces it with a new one by the same name, we need to also recreate all 1772e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // indices and triggers that refer to the files table. 1773e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // Views don't need to be recreated. 1774e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1775e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE TABLE files2 (_id INTEGER PRIMARY KEY AUTOINCREMENT," + 1776e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "_data TEXT UNIQUE" + 1777e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // the internal filesystem is case-sensitive 1778e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen (internal ? "," : " COLLATE NOCASE,") + 1779e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "_size INTEGER,format INTEGER,parent INTEGER,date_added INTEGER," + 1780e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "date_modified INTEGER,mime_type TEXT,title TEXT,description TEXT," + 1781e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "_display_name TEXT,picasa_id TEXT,orientation INTEGER,latitude DOUBLE," + 1782e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER,bucket_id TEXT," + 1783e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "bucket_display_name TEXT,isprivate INTEGER,title_key TEXT,artist_id INTEGER," + 1784e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "album_id INTEGER,composer TEXT,track INTEGER,year INTEGER CHECK(year!=0)," + 1785e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "is_ringtone INTEGER,is_music INTEGER,is_alarm INTEGER," + 1786e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "is_notification INTEGER,is_podcast INTEGER,album_artist TEXT," + 1787e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "duration INTEGER,bookmark INTEGER,artist TEXT,album TEXT,resolution TEXT," + 1788e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,name TEXT," + 1789e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "media_type INTEGER,old_id INTEGER,storage_id INTEGER,is_drm INTEGER," + 1790e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "width INTEGER, height INTEGER);"); 1791e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1792e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // copy data from old table, squashing entries with duplicate _data 1793e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("INSERT OR REPLACE INTO files2 SELECT * FROM files;"); 1794e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("DROP TABLE files;"); 1795e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("ALTER TABLE files2 RENAME TO files;"); 1796e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1797e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen // recreate indices and triggers 1798e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX album_id_idx ON files(album_id);"); 1799e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);"); 1800e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX bucket_index on files(bucket_id,media_type," + 1801e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "datetaken, _id);"); 1802e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX bucket_name on files(bucket_id,media_type," + 1803e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "bucket_display_name);"); 1804e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX format_index ON files(format);"); 1805e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX media_type_index ON files(media_type);"); 1806e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX parent_index ON files(parent);"); 1807e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX path_index ON files(_data);"); 1808e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);"); 1809e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX title_idx ON files(title);"); 1810e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE INDEX titlekey_index ON files(title_key);"); 1811e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen if (!internal) { 1812e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE TRIGGER audio_playlists_cleanup DELETE ON files" + 1813e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen " WHEN old.media_type=4" + 1814e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen " BEGIN DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 1815e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen "SELECT _DELETE_FILE(old._data);END;"); 1816e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen db.execSQL("CREATE TRIGGER files_cleanup DELETE ON files" + 1817e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen " BEGIN SELECT _OBJECT_REMOVED(old._id);END;"); 1818e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen } 1819e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen } 1820e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen 1821f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen if (fromVersion < 601) { 1822f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen // remove primary key constraint because column time is not necessarily unique 1823f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("CREATE TABLE IF NOT EXISTS log_tmp (time DATETIME, message TEXT);"); 1824f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("DELETE FROM log_tmp;"); 1825f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("INSERT INTO log_tmp SELECT time, message FROM log order by rowid;"); 1826f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("DROP TABLE log;"); 1827f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen db.execSQL("ALTER TABLE log_tmp RENAME TO log;"); 1828f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen } 1829f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen 18302a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen if (fromVersion < 700) { 18312a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen // fix datetaken fields that were added with an incorrect timestamp 18322a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen // datetaken needs to be in milliseconds, so should generally be a few orders of 18332a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen // magnitude larger than date_modified. If it's within the same order of magnitude, it 18342a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen // is probably wrong. 18352a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen // (this could do the wrong thing if your picture was actually taken before ~3/21/1970) 18362a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen db.execSQL("UPDATE files set datetaken=date_modified*1000" 18372a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen + " WHERE date_modified IS NOT NULL" 18382a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen + " AND datetaken IS NOT NULL" 18392a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen + " AND datetaken<date_modified*5;"); 18402a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen } 18412a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen 18422a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen 1843acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sanityCheck(db, fromVersion); 1844988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000; 1845988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion 1846988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen + " in " + elapsedSeconds + " seconds"); 1847988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 1848988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 1849988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen /** 1850988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen * Write a persistent diagnostic message to the log table. 1851988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen */ 1852988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen static void logToDb(SQLiteDatabase db, String message) { 1853934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen db.execSQL("INSERT OR REPLACE" + 1854934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen " INTO log (time,message) VALUES (strftime('%Y-%m-%d %H:%M:%f','now'),?);", 1855988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen new String[] { message }); 1856988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen // delete all but the last 500 rows 1857988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen db.execSQL("DELETE FROM log WHERE rowid IN" + 1858f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen " (SELECT rowid FROM log ORDER BY rowid DESC LIMIT 500,-1);"); 18591d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen } 18601d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen 18611d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen /** 1862216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen * Perform a simple sanity check on the database. Currently this tests 1863216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen * whether all the _data entries in audio_meta are unique 1864216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen */ 1865216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen private static void sanityCheck(SQLiteDatabase db, int fromVersion) { 1866216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Cursor c1 = db.query("audio_meta", new String[] {"count(*)"}, 1867216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen null, null, null, null, null); 1868216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"}, 1869216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen null, null, null, null, null); 1870216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c1.moveToFirst(); 1871216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c2.moveToFirst(); 1872216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen int num1 = c1.getInt(0); 1873216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen int num2 = c2.getInt(0); 1874216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c1.close(); 1875216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c2.close(); 1876216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen if (num1 != num2) { 1877216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Log.e(TAG, "audio_meta._data column is not unique while upgrading" + 1878216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen " from schema " +fromVersion + " : " + num1 +"/" + num2); 1879216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen // Delete all audio_meta rows so they will be rebuilt by the media scanner 1880216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen db.execSQL("DELETE FROM audio_meta;"); 1881216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } 1882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void recreateAudioView(SQLiteDatabase db) { 1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides a unified audio/artist/album info view. 1886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS audio"); 1887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " + 1888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " + 1889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;"); 1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 189195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1893677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen * Update the bucket_id and bucket_display_name columns for images and videos 1894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db 1895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param tableName 1896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1897677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen private static void updateBucketNames(SQLiteDatabase db) { 1898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Rebuild the bucket_display_name column using the natural case rather than lower case. 1899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 1900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] columns = {BaseColumns._ID, MediaColumns.DATA}; 1902677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen // update only images and videos 1903677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen Cursor cursor = db.query("files", columns, "media_type=1 OR media_type=3", 1904677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen null, null, null, null); 1905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID); 1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA); 19089491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen String [] rowId = new String[1]; 1909988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen ContentValues values = new ContentValues(); 1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor.moveToNext()) { 1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = cursor.getString(dataColumnIndex); 1912988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen rowId[0] = cursor.getString(idColumnIndex); 1913d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen if (data != null) { 1914988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen values.clear(); 1915d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen computeBucketValues(data, values); 1916677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen db.update("files", values, "_id=?", rowId); 1917d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen } else { 1918d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen Log.w(TAG, "null data at id " + rowId); 1919d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen } 1920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project cursor.close(); 1923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 1925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 1927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Iterate through the rows of a table in a database, ensuring that the 1932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * display name column has a value. 1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db 1934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param tableName 1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void updateDisplayName(SQLiteDatabase db, String tableName) { 1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Fill in default values for null displayName values 1938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME}; 1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor cursor = db.query(tableName, columns, null, null, null, null, null); 1942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID); 1944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA); 1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME); 1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(); 1947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor.moveToNext()) { 1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String displayName = cursor.getString(displayNameIndex); 1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (displayName == null) { 1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = cursor.getString(dataColumnIndex); 1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.clear(); 1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project computeDisplayName(data, values); 1953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int rowId = cursor.getInt(idColumnIndex); 1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.update(tableName, values, "_id=" + rowId, null); 1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project cursor.close(); 1959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 1961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 1963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1965988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 1966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param data The input path 1968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param values the content values, where the bucked id name and bucket display name are updated. 1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void computeBucketValues(String data, ContentValues values) { 1972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File parentFile = new File(data).getParentFile(); 1973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (parentFile == null) { 1974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project parentFile = new File("/"); 1975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Lowercase the path for hashing. This avoids duplicate buckets if the 1978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // filepath case is changed externally. 1979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Keep the original case for display. 1980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String path = parentFile.toString().toLowerCase(); 1981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name = parentFile.getName(); 1982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the 1984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // same for both images and video. However, for backwards-compatibility reasons 1985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // there is no common base class. We use the ImageColumns version here 1986d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood values.put(ImageColumns.BUCKET_ID, path.hashCode()); 1987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(ImageColumns.BUCKET_DISPLAY_NAME, name); 1988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param data The input path 1992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param values the content values, where the display name is updated. 1993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 1994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void computeDisplayName(String data, ContentValues values) { 1996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = (data == null ? "" : data.toString()); 1997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int idx = s.lastIndexOf('/'); 1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (idx >= 0) { 1999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project s = s.substring(idx + 1); 2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("_display_name", s); 2002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2004b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen /** 2005498b62c2912302a23532c73a028a7684c5df33caRay Chen * Copy taken time from date_modified if we lost the original value (e.g. after factory reset) 2006498b62c2912302a23532c73a028a7684c5df33caRay Chen * This works for both video and image tables. 2007b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen * 2008b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen * @param values the content values, where taken time is updated. 2009b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen */ 2010b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen private static void computeTakenTime(ContentValues values) { 2011b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen if (! values.containsKey(Images.Media.DATE_TAKEN)) { 2012b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen // This only happens when MediaScanner finds an image file that doesn't have any useful 2013b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen // reference to get this value. (e.g. GPSTimeStamp) 2014498b62c2912302a23532c73a028a7684c5df33caRay Chen Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED); 2015b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen if (lastModified != null) { 2016b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen values.put(Images.Media.DATE_TAKEN, lastModified * 1000); 2017b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 2018b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 2019b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 2020b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen 2021b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen /** 2022b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * This method blocks until thumbnail is ready. 2023b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * 2024b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * @param thumbUri 2025b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * @return 2026b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen */ 2027b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private boolean waitForThumbnailReady(Uri origUri) { 2028b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA, 2029b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ImageColumns.MINI_THUMB_MAGIC}, null, null, null); 2030b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c == null) return false; 2031b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2032b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean result = false; 2033b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2034b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c.moveToFirst()) { 2035e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen long id = c.getLong(0); 2036b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String path = c.getString(1); 2037b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen long magic = c.getLong(2); 2038b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 20399299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen MediaThumbRequest req = requestMediaThumbnail(path, origUri, 20409299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen MediaThumbRequest.PRIORITY_HIGH, magic); 20419299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen if (req == null) { 20429299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen return false; 20439299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } 20449299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen synchronized (req) { 20459299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen try { 20469299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen while (req.mState == MediaThumbRequest.State.WAIT) { 20479299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen req.wait(); 204820434e032e498b716f87cce2f23dd646819218bfRay Chen } 20499299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } catch (InterruptedException e) { 20509299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen Log.w(TAG, e); 20519299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } 20529299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen if (req.mState == MediaThumbRequest.State.DONE) { 20539299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen result = true; 2054b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2055b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2056b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2057b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen c.close(); 2058b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2059b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return result; 2060b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2061b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2062e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid, 2063e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean isVideo) { 2064e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean cancelAllOrigId = (id == -1); 2065e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean cancelAllGroupId = (gid == -1); 2066e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen return (req.mCallingPid == pid) && 2067e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (cancelAllGroupId || req.mGroupId == gid) && 2068e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (cancelAllOrigId || req.mOrigId == id) && 2069e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (req.mIsVideo == isVideo); 2070e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } 2071e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 2072b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table, 2073b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String column, boolean hasThumbnailId) { 2074b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.setTables(table); 2075b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (hasThumbnailId) { 2076b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // For uri dispatched to this method, the 4th path segment is always 2077b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // the thumbnail id. 2078b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.appendWhere("_id = " + uri.getPathSegments().get(3)); 2079b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // client already knows which thumbnail it wants, bypass it. 2080b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 2081b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2082b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String origId = uri.getQueryParameter("orig_id"); 2083b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // We can't query ready_flag unless we know original id 2084b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (origId == null) { 2085b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // this could be thumbnail query for other purpose, bypass it. 2086b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 2087b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2088b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2089b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean needBlocking = "1".equals(uri.getQueryParameter("blocking")); 209020434e032e498b716f87cce2f23dd646819218bfRay Chen boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel")); 2091e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Uri origUri = uri.buildUpon().encodedPath( 2092e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen uri.getPath().replaceFirst("thumbnails", "media")) 2093e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen .appendPath(origId).build(); 2094b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2095b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (needBlocking && !waitForThumbnailReady(origUri)) { 209620434e032e498b716f87cce2f23dd646819218bfRay Chen Log.w(TAG, "original media doesn't exist or it's canceled."); 2097b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return false; 209820434e032e498b716f87cce2f23dd646819218bfRay Chen } else if (cancelRequest) { 2099e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen String groupId = uri.getQueryParameter("group_id"); 2100e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean isVideo = "video".equals(uri.getPathSegments().get(1)); 210120434e032e498b716f87cce2f23dd646819218bfRay Chen int pid = Binder.getCallingPid(); 210220434e032e498b716f87cce2f23dd646819218bfRay Chen long id = -1; 2103e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen long gid = -1; 2104e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 210520434e032e498b716f87cce2f23dd646819218bfRay Chen try { 210620434e032e498b716f87cce2f23dd646819218bfRay Chen id = Long.parseLong(origId); 2107e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen gid = Long.parseLong(groupId); 210820434e032e498b716f87cce2f23dd646819218bfRay Chen } catch (NumberFormatException ex) { 210920434e032e498b716f87cce2f23dd646819218bfRay Chen // invalid cancel request 211020434e032e498b716f87cce2f23dd646819218bfRay Chen return false; 211120434e032e498b716f87cce2f23dd646819218bfRay Chen } 2112e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 211320434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mMediaThumbQueue) { 2114e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen if (mCurrentThumbRequest != null && 2115e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) { 211620434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mCurrentThumbRequest) { 211720434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL; 211820434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.notifyAll(); 211920434e032e498b716f87cce2f23dd646819218bfRay Chen } 212020434e032e498b716f87cce2f23dd646819218bfRay Chen } 212120434e032e498b716f87cce2f23dd646819218bfRay Chen for (MediaThumbRequest mtq : mMediaThumbQueue) { 2122e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen if (matchThumbRequest(mtq, pid, id, gid, isVideo)) { 212320434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mtq) { 212420434e032e498b716f87cce2f23dd646819218bfRay Chen mtq.mState = MediaThumbRequest.State.CANCEL; 212520434e032e498b716f87cce2f23dd646819218bfRay Chen mtq.notifyAll(); 212620434e032e498b716f87cce2f23dd646819218bfRay Chen } 212720434e032e498b716f87cce2f23dd646819218bfRay Chen 212820434e032e498b716f87cce2f23dd646819218bfRay Chen mMediaThumbQueue.remove(mtq); 212920434e032e498b716f87cce2f23dd646819218bfRay Chen } 213020434e032e498b716f87cce2f23dd646819218bfRay Chen } 213120434e032e498b716f87cce2f23dd646819218bfRay Chen } 2132b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2133b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2134b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (origId != null) { 2135b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.appendWhere(column + " = " + origId); 2136b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2137b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 2138b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 213901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 214001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen @Override 214101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen public Uri canonicalize(Uri uri) { 214201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen int match = URI_MATCHER.match(uri); 214301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 214401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // only support canonicalizing specific audio Uris 214501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (match != AUDIO_MEDIA_ID) { 214601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 214701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 214801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 214901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen Cursor c = query(uri, null, null, null, null); 215001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (c == null) { 215101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 215201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 215301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (c.getCount() != 1 || !c.moveToNext()) { 215401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen c.close(); 215501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 215601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 215701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 215801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // Construct a canonical Uri by tacking on some query parameters 215901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen Uri.Builder builder = uri.buildUpon(); 216001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen builder.appendQueryParameter(CANONICAL, "1"); 216101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen String title = c.getString(c.getColumnIndex(MediaStore.Audio.Media.TITLE)); 216201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen c.close(); 216301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (TextUtils.isEmpty(title)) { 216401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 216501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 216601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen builder.appendQueryParameter(MediaStore.Audio.Media.TITLE, title); 216701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen Uri newUri = builder.build(); 216801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return newUri; 216901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 217001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 217101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen @Override 217201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen public Uri uncanonicalize(Uri uri) { 217301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (uri != null && "1".equals(uri.getQueryParameter(CANONICAL))) { 217401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen int match = URI_MATCHER.match(uri); 217501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (match != AUDIO_MEDIA_ID) { 217601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // this type of canonical Uri is not supported 217701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 217801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 217901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen String titleFromUri = uri.getQueryParameter(MediaStore.Audio.Media.TITLE); 218001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (titleFromUri == null) { 218101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // the required parameter is missing 218201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 218301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 218401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // clear the query parameters, we don't need them anymore 218501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen uri = uri.buildUpon().clearQuery().build(); 218601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 218701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen Cursor c = query(uri, null, null, null, null); 218801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 218901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen int titleIdx = c.getColumnIndex(MediaStore.Audio.Media.TITLE); 219001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (c != null && c.getCount() == 1 && c.moveToNext() && 219101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen titleFromUri.equals(c.getString(titleIdx))) { 219201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // the result matched perfectly 219301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen c.close(); 219401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return uri; 219501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 219601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 219701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen c.close(); 219801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // do a lookup by title 219901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen Uri newUri = MediaStore.Audio.Media.getContentUri(uri.getPathSegments().get(0)); 220001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 220101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen c = query(newUri, null, MediaStore.Audio.Media.TITLE + "=?", 220201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen new String[] {titleFromUri}, null); 220301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (c == null) { 220401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 220501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 220601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (!c.moveToNext()) { 220701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen c.close(); 220801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return null; 220901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 221001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen // get the first matching entry and return a Uri for it 221101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen long id = c.getLong(c.getColumnIndex(MediaStore.Audio.Media._ID)); 221201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen c.close(); 221301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return ContentUris.withAppendedId(newUri, id); 221401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 221501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return uri; 221601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 221701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 221801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen private Uri safeUncanonicalize(Uri uri) { 221901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen Uri newUri = uncanonicalize(uri); 222001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen if (newUri != null) { 222101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return newUri; 222201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 222301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen return uri; 222401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen } 222501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 2226b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen @SuppressWarnings("fallthrough") 2227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public Cursor query(Uri uri, String[] projectionIn, String selection, 2229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] selectionArgs, String sort) { 223001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 223101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen uri = safeUncanonicalize(uri); 223201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen 2233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int table = URI_MATCHER.match(uri); 2234baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen List<String> prependArgs = new ArrayList<String>(); 2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 223601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // Log.v(TAG, "query: uri="+uri+", selection="+selection); 2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 2238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (table == MEDIA_SCANNER) { 2239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mMediaScannerVolume == null) { 2240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 2241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 2242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create a cursor to return volume currently being scanned by the media scanner 22430027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME}); 22440027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen c.addRow(new String[] {mMediaScannerVolume}); 22450027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen return c; 2246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 22490027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // Used temporarily (until we have unique media IDs) to get an identifier 22500027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // for the current sd card, so that the music app doesn't have to use the 22510027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // non-public getFatVolumeId method 22520027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen if (table == FS_ID) { 22530027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen MatrixCursor c = new MatrixCursor(new String[] {"fsid"}); 22540027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen c.addRow(new Integer[] {mVolumeId}); 22550027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen return c; 22560027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen } 22570027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 2258704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen if (table == VERSION) { 2259704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen MatrixCursor c = new MatrixCursor(new String[] {"version"}); 226090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen c.addRow(new Integer[] {getDatabaseVersion(getContext())}); 2261704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen return c; 2262704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen } 2263704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen 2264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String groupBy = null; 226510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 226610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null) { 2267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 2268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 226910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 227010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getReadableDatabase(); 22715fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) return null; 2272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 22734574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String limit = uri.getQueryParameter("limit"); 2274c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String filter = uri.getQueryParameter("filter"); 2275c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String [] keywords = null; 2276c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (filter != null) { 2277c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen filter = Uri.decode(filter).trim(); 2278c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (!TextUtils.isEmpty(filter)) { 2279c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String [] searchWords = filter.split(" "); 2280c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen keywords = new String[searchWords.length]; 2281c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; i < searchWords.length; i++) { 2282c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String key = MediaStore.Audio.keyFor(searchWords[i]); 2283c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("\\", "\\\\"); 2284c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("%", "\\%"); 2285c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("_", "\\_"); 2286c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen keywords[i] = key; 2287c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2288c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2289c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2290db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin if (uri.getQueryParameter("distinct") != null) { 2291db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin qb.setDistinct(true); 2292db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin } 2293c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen 2294b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean hasThumbnailId = false; 2295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (table) { 2297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 2298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("images"); 2299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (uri.getQueryParameter("distinct") != null) 2300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setDistinct(true); 2301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // set the project map so that data dir is prepended to _data. 2303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project //qb.setProjectionMap(mImagesProjectionMap, true); 2304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 2307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("images"); 2308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (uri.getQueryParameter("distinct") != null) 2309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setDistinct(true); 2310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // set the project map so that data dir is prepended to _data. 2312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project //qb.setProjectionMap(mImagesProjectionMap, true); 2313baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2314baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS_ID: 2318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen hasThumbnailId = true; 2319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS: 2320b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) { 2321b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return null; 2322b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 2326d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 2327ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.equalsIgnoreCase("is_music=1") 2328ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen || selection.equalsIgnoreCase("is_podcast=1") ) 2329c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 2330c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 2331ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting songs"); 2332ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 2333ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 2334ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio"); 2335c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2336c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 2337c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2338c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2339c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2340c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 2341baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'"); 2342baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2343c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2344ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 2345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 2348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio"); 2349baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2350baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id IN (SELECT genre_id FROM " + 2356baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "audio_genres_map WHERE audio_id=?)"); 2357baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 2361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2362baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2363baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(5)); 2364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 2367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id IN (SELECT playlist_id FROM " + 2369baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "audio_playlists_map WHERE audio_id=?)"); 2370baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 2374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2375baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2376baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(5)); 2377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 2380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 2384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 2385baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2386baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2389bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen case AUDIO_GENRES_ALL_MEMBERS: 2390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 239178b2885edc406273d688536b0eadfea006b20662Marco Nelissen { 239278b2885edc406273d688536b0eadfea006b20662Marco Nelissen // if simpleQuery is true, we can do a simpler query on just audio_genres_map 239378b2885edc406273d688536b0eadfea006b20662Marco Nelissen // we can do this if we have no keywords and our projection includes just columns 239478b2885edc406273d688536b0eadfea006b20662Marco Nelissen // from audio_genres_map 239578b2885edc406273d688536b0eadfea006b20662Marco Nelissen boolean simpleQuery = (keywords == null && projectionIn != null 239678b2885edc406273d688536b0eadfea006b20662Marco Nelissen && (selection == null || selection.equalsIgnoreCase("genre_id=?"))); 239778b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (projectionIn != null) { 239878b2885edc406273d688536b0eadfea006b20662Marco Nelissen for (int i = 0; i < projectionIn.length; i++) { 239978b2885edc406273d688536b0eadfea006b20662Marco Nelissen String p = projectionIn[i]; 240078b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (p.equals("_id")) { 240178b2885edc406273d688536b0eadfea006b20662Marco Nelissen // note, this is different from playlist below, because 240278b2885edc406273d688536b0eadfea006b20662Marco Nelissen // "_id" used to (wrongly) be the audio id in this query, not 240378b2885edc406273d688536b0eadfea006b20662Marco Nelissen // the row id of the entry in the map, and we preserve this 240478b2885edc406273d688536b0eadfea006b20662Marco Nelissen // behavior for backwards compatibility 240578b2885edc406273d688536b0eadfea006b20662Marco Nelissen simpleQuery = false; 240678b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 240778b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (simpleQuery && !(p.equals("audio_id") || 240878b2885edc406273d688536b0eadfea006b20662Marco Nelissen p.equals("genre_id"))) { 240978b2885edc406273d688536b0eadfea006b20662Marco Nelissen simpleQuery = false; 241078b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 241178b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 241278b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 241378b2885edc406273d688536b0eadfea006b20662Marco Nelissen if (simpleQuery) { 241478b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.setTables("audio_genres_map_noid"); 2415bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen if (table == AUDIO_GENRES_ID_MEMBERS) { 2416baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("genre_id=?"); 2417baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2418bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen } 241978b2885edc406273d688536b0eadfea006b20662Marco Nelissen } else { 242078b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.setTables("audio_genres_map_noid, audio"); 2421bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen qb.appendWhere("audio._id = audio_id"); 2422bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen if (table == AUDIO_GENRES_ID_MEMBERS) { 2423baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere(" AND genre_id=?"); 2424baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2425bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen } 242678b2885edc406273d688536b0eadfea006b20662Marco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 242778b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.appendWhere(" AND "); 242878b2885edc406273d688536b0eadfea006b20662Marco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 242978b2885edc406273d688536b0eadfea006b20662Marco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 243078b2885edc406273d688536b0eadfea006b20662Marco Nelissen "||" + MediaStore.Audio.Media.TITLE_KEY + 2431baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2432baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 243378b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 243478b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 243578b2885edc406273d688536b0eadfea006b20662Marco Nelissen } 2436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 2439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 2443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 2444baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2445baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2448b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 2449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 2450e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // if simpleQuery is true, we can do a simpler query on just audio_playlists_map 2451e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // we can do this if we have no keywords and our projection includes just columns 2452e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // from audio_playlists_map 24534382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood boolean simpleQuery = (keywords == null && projectionIn != null 24544382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood && (selection == null || selection.equalsIgnoreCase("playlist_id=?"))); 245597e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen if (projectionIn != null) { 245697e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen for (int i = 0; i < projectionIn.length; i++) { 2457e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood String p = projectionIn[i]; 2458e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (simpleQuery && !(p.equals("audio_id") || 2459e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood p.equals("playlist_id") || p.equals("play_order"))) { 2460e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood simpleQuery = false; 2461e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } 2462e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (p.equals("_id")) { 246397e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen projectionIn[i] = "audio_playlists_map._id AS _id"; 246497e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen } 2465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2467e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (simpleQuery) { 2468e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.setTables("audio_playlists_map"); 2469baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("playlist_id=?"); 2470baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2471e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } else { 2472e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.setTables("audio_playlists_map, audio"); 2473baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("audio._id = audio_id AND playlist_id=?"); 2474baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2475e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood for (int i = 0; keywords != null && i < keywords.length; i++) { 2476e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere(" AND "); 2477e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2478e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood "||" + MediaStore.Audio.Media.ALBUM_KEY + 2479e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood "||" + MediaStore.Audio.Media.TITLE_KEY + 2480baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2481baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2482e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } 2483c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2484b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) { 2485baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere(" AND audio_playlists_map._id=?"); 2486baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(5)); 2487b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen } 2488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 2491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("video"); 2492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 2494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("video"); 2495baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2496baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2499b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS_ID: 2500b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen hasThumbnailId = true; 2501b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: 2502b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) { 2503b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return null; 2504b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2505b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 2506b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS: 2508d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 2509ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.length() == 0) 2510c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 2511c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 2512ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting artists"); 2513ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 2514ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen projectionIn[0] = "count(distinct artist_id)"; 2515ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.appendWhere("is_music=1"); 2516ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 2517ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("artist_info"); 2518c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2519c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 2520c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2521c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2522c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2523baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2524baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2525c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2526ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 2527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS_ID: 2530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("artist_info"); 2531baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2532baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS_ID_ALBUMS: 2536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String aid = uri.getPathSegments().get(3); 2537acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen qb.setTables("audio LEFT OUTER JOIN album_art ON" + 2538acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen " audio.album_id=album_art.album_id"); 2539acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " + 2540baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen "artists_albums_map WHERE artist_id=?)"); 2541baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(aid); 2542c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2543c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2544c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2545c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 2546baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2547baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2548c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2549acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen groupBy = "audio.album_id"; 2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST, 2551acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " + 2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST); 2553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setProjectionMap(sArtistAlbumsMap); 2554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMS: 2557d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 2558ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.length() == 0) 2559c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 2560c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 2561ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting albums"); 2562ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 2563ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen projectionIn[0] = "count(distinct album_id)"; 2564ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.appendWhere("is_music=1"); 2565ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 2566ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("album_info"); 2567c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 2568c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 2569c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 2570c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2571c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 2572c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 2573baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen " LIKE ? ESCAPE '\\'"); 2574baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add("%" + keywords[i] + "%"); 2575c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 2576ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 2577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMS_ID: 2580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("album_info"); 2581baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2582baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMART_ID: 2586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("album_art"); 2587baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("album_id=?"); 2588baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(3)); 2589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2591a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_LEGACY: 2592a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen Log.w(TAG, "Legacy media search Uri used. Please update your code."); 2593a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // fall through 2594a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_FANCY: 2595a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_BASIC: 2596baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen return doAudioSearch(db, qb, uri, projectionIn, selection, 2597baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combine(prependArgs, selectionArgs), sort, table, limit); 2598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 259916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES_ID: 2600e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 2601baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen qb.appendWhere("_id=?"); 2602baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen prependArgs.add(uri.getPathSegments().get(2)); 2603b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // fall through 260416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES: 2605e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 260616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood qb.setTables("files"); 2607b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood break; 2608b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2609e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood case MTP_OBJECT_REFERENCES: 2610e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int handle = Integer.parseInt(uri.getPathSegments().get(2)); 261110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen return getObjectReferences(helper, db, handle); 2612e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 2613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 2614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalStateException("Unknown URL: " + uri.toString()); 2615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2617baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection, 2618baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen // combine(prependArgs, selectionArgs), groupBy, null, sort, limit)); 2619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = qb.query(db, projectionIn, selection, 2620baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combine(prependArgs, selectionArgs), groupBy, null, sort, limit); 2621b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (c != null) { 2623ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen String nonotify = uri.getQueryParameter("nonotify"); 2624ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen if (nonotify == null || !nonotify.equals("1")) { 2625ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen c.setNotificationUri(getContext().getContentResolver(), uri); 2626ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen } 2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2628b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return c; 2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2632baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen private String[] combine(List<String> prepend, String[] userArgs) { 2633baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen int presize = prepend.size(); 2634baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen if (presize == 0) { 2635baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen return userArgs; 2636baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2637baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen 2638baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen int usersize = (userArgs != null) ? userArgs.length : 0; 2639baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen String [] combined = new String[presize + usersize]; 2640baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen for (int i = 0; i < presize; i++) { 2641baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combined[i] = prepend.get(i); 2642baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2643baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen for (int i = 0; i < usersize; i++) { 2644baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen combined[presize + i] = userArgs[i]; 2645baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2646baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen return combined; 2647baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen } 2648baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen 2649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb, 2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri, String[] projectionIn, String selection, 26514574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String[] selectionArgs, String sort, int mode, 26524574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String limit) { 2653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 265418c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment(); 2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mSearchString = mSearchString.replaceAll(" ", " ").trim().toLowerCase(); 2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String [] searchWords = mSearchString.length() > 0 ? 2658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mSearchString.split(" ") : new String[0]; 2659a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String [] wildcardWords = new String[searchWords.length]; 2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int len = searchWords.length; 2661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < len; i++) { 2662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Because we match on individual words here, we need to remove words 2663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // like 'a' and 'the' that aren't part of the keys. 26643001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen String key = MediaStore.Audio.keyFor(searchWords[i]); 26653001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("\\", "\\\\"); 26663001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("%", "\\%"); 26673001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("_", "\\_"); 2668a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen wildcardWords[i] = 2669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project (searchWords[i].equals("a") || searchWords[i].equals("an") || 26703001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen searchWords[i].equals("the")) ? "%" : "%" + key + "%"; 2671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2673a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String where = ""; 2674a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen for (int i = 0; i < searchWords.length; i++) { 2675a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (i == 0) { 26763001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen where = "match LIKE ? ESCAPE '\\'"; 2677a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else { 26783001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen where += " AND match LIKE ? ESCAPE '\\'"; 2679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2682a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen qb.setTables("search"); 2683a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String [] cols; 2684a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (mode == AUDIO_SEARCH_FANCY) { 2685a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsFancy; 2686a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else if (mode == AUDIO_SEARCH_BASIC) { 2687a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsBasic; 2688a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else { 2689a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsLegacy; 2690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 26914574e03055af60fada50481f2b34e19a687d5866Marco Nelissen return qb.query(db, cols, where, wildcardWords, null, null, null, limit); 2692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String getType(Uri url) 2696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 2697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (URI_MATCHER.match(url)) { 2698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 2699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 2700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 2701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 2702c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood case FILES_ID: 270326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen Cursor c = null; 270426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen try { 270526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c = query(url, MIME_TYPE_PROJECTION, null, null, null); 270626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen if (c != null && c.getCount() == 1) { 270726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.moveToFirst(); 270826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen String mimeType = c.getString(1); 270926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.deactivate(); 271026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen return mimeType; 271126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } 271226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } finally { 271326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen if (c != null) { 271426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.close(); 271526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } 2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 2720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS: 2721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Images.Media.CONTENT_TYPE; 2722804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen case AUDIO_ALBUMART_ID: 2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS_ID: 2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return "image/jpeg"; 2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 2727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 2728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 2729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Media.CONTENT_TYPE; 2730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 2732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Genres.CONTENT_TYPE; 2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 2735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 2736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Genres.ENTRY_CONTENT_TYPE; 2737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 2738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 2739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Playlists.CONTENT_TYPE; 2740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 2741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 2742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Playlists.ENTRY_CONTENT_TYPE; 2743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 2745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Video.Media.CONTENT_TYPE; 2746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2747804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen throw new IllegalStateException("Unknown URL : " + url); 2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 2751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Ensures there is a file in the _data column of values, if one isn't 275250c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen * present a new filename is generated. The file itself is not created. 2753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 2754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param initialValues the values passed to insert by the caller 2755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return the new values 2756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 2757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private ContentValues ensureFile(boolean internal, ContentValues initialValues, 2758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String preferredExtension, String directoryName) { 2759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values; 2760801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String file = initialValues.getAsString(MediaStore.MediaColumns.DATA); 2761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (TextUtils.isEmpty(file)) { 2762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file = generateFileName(internal, preferredExtension, directoryName); 2763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = new ContentValues(initialValues); 2764801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood values.put(MediaStore.MediaColumns.DATA, file); 2765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 2766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = initialValues; 2767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 276992013781ab1573eee3d5d75682f320fe3a6076f2Marco Nelissen // we used to create the file here, but now defer this until openFile() is called 2770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return values; 2771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2773d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private void sendObjectAdded(long objectHandle) { 277434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 277534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 277634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood try { 277734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService.sendObjectAdded((int)objectHandle); 277834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } catch (RemoteException e) { 277934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Log.e(TAG, "RemoteException in sendObjectAdded", e); 278034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 278134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 2782d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2783d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2784d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2785d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 2786d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private void sendObjectRemoved(long objectHandle) { 278734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 278834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 278934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood try { 279034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService.sendObjectRemoved((int)objectHandle); 279134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } catch (RemoteException e) { 279234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Log.e(TAG, "RemoteException in sendObjectRemoved", e); 279334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 279434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 2795d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2796d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2797d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2798d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 2799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int bulkInsert(Uri uri, ContentValues values[]) { 2801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 2802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == VOLUMES) { 2803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return super.bulkInsert(uri, values); 2804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 280510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 280610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null) { 2807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 2808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 2809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 281010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 28115fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 28125fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 28135fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 2814ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2815ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) { 2816ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen return playlistBulkInsert(db, uri, values); 2817e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } else if (match == MTP_OBJECT_REFERENCES) { 2818e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int handle = Integer.parseInt(uri.getPathSegments().get(2)); 281910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen return setObjectReferences(helper, db, handle, values); 2820ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2821ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 28222dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 2823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 28242dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke ArrayList<Long> notifyRowIds = new ArrayList<Long>(); 2825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int numInserted = 0; 2826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 2827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int len = values.length; 2828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < len; i++) { 2829801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood if (values[i] != null) { 28302dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke insertInternal(uri, match, values[i], notifyRowIds); 2831801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood } 2832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project numInserted = len; 2834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 2835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 2836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 2837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 28382dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 28392dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke // Notify MTP (outside of successful transaction) 28402dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke notifyMtp(notifyRowIds); 28412dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 2842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 2843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return numInserted; 2844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2847801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood public Uri insert(Uri uri, ContentValues initialValues) { 28485d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood int match = URI_MATCHER.match(uri); 28492dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 28502dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke ArrayList<Long> notifyRowIds = new ArrayList<Long>(); 28512dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds); 28522dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke notifyMtp(notifyRowIds); 28532dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 28545d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood // do not signal notification for MTP objects. 28555d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood // we will signal instead after file transfer is successful. 2856e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood if (newUri != null && match != MTP_OBJECTS) { 2857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 2858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return newUri; 2860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 28622dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke private void notifyMtp(ArrayList<Long> rowIds) { 28632dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke int size = rowIds.size(); 28642dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke for (int i = 0; i < size; i++) { 28652dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke sendObjectAdded(rowIds.get(i).longValue()); 28662dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke } 28672dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke } 28682dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke 2869ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) { 2870ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen DatabaseUtils.InsertHelper helper = 2871ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen new DatabaseUtils.InsertHelper(db, "audio_playlists_map"); 28728b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID); 28738b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID); 28748b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER); 28758b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen long playlistId = Long.parseLong(uri.getPathSegments().get(3)); 2876ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2877ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.beginTransaction(); 2878ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen int numInserted = 0; 2879ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen try { 2880ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen int len = values.length; 2881ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen for (int i = 0; i < len; i++) { 28828b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.prepareForInsert(); 28838b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // getting the raw Object and converting it long ourselves saves 28848b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // an allocation (the alternative is ContentValues.getAsLong, which 28858b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // returns a Long object) 28868b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen long audioid = ((Number) values[i].get( 28878b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue(); 28888b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(audioidcolidx, audioid); 28898b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(playlistididx, playlistId); 28908b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // convert to int ourselves to save an allocation. 28918b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playorder = ((Number) values[i].get( 28928b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue(); 28938b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(playorderidx, playorder); 28948b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.execute(); 2895ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2896ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen numInserted = len; 2897ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.setTransactionSuccessful(); 2898ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } finally { 2899ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.endTransaction(); 2900ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen helper.close(); 2901ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2902ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen getContext().getContentResolver().notifyChange(uri, null); 2903ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen return numInserted; 2904ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2905ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 290610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) { 290710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path); 2908ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood ContentValues values = new ContentValues(); 2909ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION); 2910ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood values.put(FileColumns.DATA, path); 291110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values.put(FileColumns.PARENT, getParent(helper, db, path)); 29129be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood values.put(FileColumns.STORAGE_ID, getStorageId(path)); 2913f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood File file = new File(path); 2914f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (file.exists()) { 2915f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000); 2916f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood } 291710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 2918ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values); 2919ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood sendObjectAdded(rowId); 2920ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood return rowId; 2921ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 2922ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 292310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) { 2924b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood int lastSlash = path.lastIndexOf('/'); 2925b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (lastSlash > 0) { 2926b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood String parentPath = path.substring(0, lastSlash); 29279be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood for (int i = 0; i < mExternalStoragePaths.length; i++) { 29289be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (parentPath.equals(mExternalStoragePaths[i])) { 29299be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return 0; 29309be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 2931b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 29327f36494e085c26c69cd5925e54028822025eff29Marco Nelissen Long cid = mDirectoryCache.get(parentPath); 29337f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (cid != null) { 29347f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath); 29357f36494e085c26c69cd5925e54028822025eff29Marco Nelissen return cid; 29367f36494e085c26c69cd5925e54028822025eff29Marco Nelissen } 29377f36494e085c26c69cd5925e54028822025eff29Marco Nelissen 2938e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen String selection = MediaStore.MediaColumns.DATA + "=?"; 2939b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood String [] selargs = { parentPath }; 294010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 29417f36494e085c26c69cd5925e54028822025eff29Marco Nelissen Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null); 2942b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood try { 29437f36494e085c26c69cd5925e54028822025eff29Marco Nelissen long id; 2944b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (c == null || c.getCount() == 0) { 2945b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // parent isn't in the database - so add it 294610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen id = insertDirectory(helper, db, parentPath); 29477f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath); 2948b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } else { 29495461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen if (c.getCount() > 1) { 29505461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen Log.e(TAG, "more than one match for " + parentPath); 29515461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } 2952b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood c.moveToFirst(); 29537f36494e085c26c69cd5925e54028822025eff29Marco Nelissen id = c.getLong(0); 29547f36494e085c26c69cd5925e54028822025eff29Marco Nelissen if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath); 2955b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 29567f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.put(parentPath, id); 29577f36494e085c26c69cd5925e54028822025eff29Marco Nelissen return id; 2958b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } finally { 2959b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (c != null) c.close(); 2960b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2961b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } else { 2962b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood return 0; 2963b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2964b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2965b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 29669be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood private int getStorageId(String path) { 29679be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood for (int i = 0; i < mExternalStoragePaths.length; i++) { 29689be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood String test = mExternalStoragePaths[i]; 29699be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (path.startsWith(test)) { 29709be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood int length = test.length(); 29719be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (path.length() == length || path.charAt(length) == '/') { 29729be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return MtpStorage.getStorageId(i); 29739be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 29749be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 29759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 29769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood // default to primary storage 29779be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return MtpStorage.getStorageId(0); 29789be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 29799be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood 298010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType, 29812dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke boolean notify, ArrayList<Long> notifyRowIds) { 298210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 2983afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood ContentValues values = null; 2984afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2985afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood switch (mediaType) { 2986afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_IMAGE: { 298710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values = ensureFile(helper.mInternal, initialValues, ".jpg", "DCIM/Camera"); 2988afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2989afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000); 2990afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String data = values.getAsString(MediaColumns.DATA); 2991afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (! values.containsKey(MediaColumns.DISPLAY_NAME)) { 2992afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeDisplayName(data, values); 2993afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2994afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeTakenTime(values); 2995afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 2996afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2997afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2998afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_AUDIO: { 2999afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // SQLite Views are read-only, so we need to deconstruct this 3000afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // insert and do inserts into the underlying tables. 3001afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // If doing this here turns out to be a performance bottleneck, 3002afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // consider moving this to native code and using triggers on 3003afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // the view. 3004afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(initialValues); 3005afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 30062658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST); 30072658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION); 30082658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen values.remove(MediaStore.Audio.Media.COMPILATION); 30092658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 3010afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Insert the artist into the artist table and remove it from 3011afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // the input values 3012afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Object so = values.get("artist"); 3013afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String s = (so == null ? "" : so.toString()); 3014afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("artist"); 3015afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long artistRowId; 301610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> artistCache = helper.mArtistCache; 3017801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String path = values.getAsString(MediaStore.MediaColumns.DATA); 3018afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood synchronized(artistCache) { 3019afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long temp = artistCache.get(s); 3020afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (temp == null) { 302110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen artistRowId = getKeyIdForName(helper, db, 302210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "artists", "artist_key", "artist", 3023afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s, s, path, 0, null, artistCache, uri); 3024afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 3025afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood artistRowId = temp.longValue(); 3026afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3027afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3028afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String artist = s; 3029afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3030afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Do the same for the album field 3031afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood so = values.get("album"); 3032afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s = (so == null ? "" : so.toString()); 3033afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("album"); 3034afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long albumRowId; 303510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> albumCache = helper.mAlbumCache; 3036afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood synchronized(albumCache) { 30372658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen int albumhash = 0; 30382658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (albumartist != null) { 30392658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumhash = albumartist.hashCode(); 30402658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else if (compilation != null && compilation.equals("1")) { 30412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // nothing to do, hash already set 30422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else { 30432658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumhash = path.substring(0, path.lastIndexOf('/')).hashCode(); 30442658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } 3045afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String cacheName = s + albumhash; 3046afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long temp = albumCache.get(cacheName); 3047afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (temp == null) { 304810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen albumRowId = getKeyIdForName(helper, db, 304910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "albums", "album_key", "album", 3050afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s, cacheName, path, albumhash, artist, albumCache, uri); 3051afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 3052afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood albumRowId = temp; 3053afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3054afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 30555d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood 3056afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("artist_id", Integer.toString((int)artistRowId)); 3057afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("album_id", Integer.toString((int)albumRowId)); 3058afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood so = values.getAsString("title"); 3059afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s = (so == null ? "" : so.toString()); 3060afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("title_key", MediaStore.Audio.keyFor(s)); 3061afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // do a final trim of the title, in case it started with the special 3062afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // "sort first" character (ascii \001) 3063afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("title"); 3064afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("title", s.trim()); 3065b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 3066801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values); 3067afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 3068afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3069afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3070afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_VIDEO: { 307110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values = ensureFile(helper.mInternal, initialValues, ".3gp", "video"); 3072801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String data = values.getAsString(MediaStore.MediaColumns.DATA); 3073afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeDisplayName(data, values); 3074afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeTakenTime(values); 3075afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 3076afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3077afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3078afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3079afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (values == null) { 3080afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(initialValues); 3081afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3082fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // compute bucket_id and bucket_display_name for all files 3083afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String path = values.getAsString(MediaStore.MediaColumns.DATA); 3084fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood if (path != null) { 3085fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood computeBucketValues(path, values); 3086fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood } 3087fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000); 3088afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3089afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long rowId = 0; 3090afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Integer i = values.getAsInteger( 3091afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID); 3092afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (i != null) { 3093afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = i.intValue(); 3094afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(values); 3095afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID); 3096afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3097afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3098afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String title = values.getAsString(MediaStore.MediaColumns.TITLE); 30993572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen if (title == null && path != null) { 3100c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood title = MediaFile.getFileTitle(path); 3101c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3102c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.TITLE, title); 3103c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 3104afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE); 3105afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Integer formatObject = values.getAsInteger(FileColumns.FORMAT); 3106c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood int format = (formatObject == null ? 0 : formatObject.intValue()); 3107c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (format == 0) { 310863b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang if (TextUtils.isEmpty(path)) { 3109c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood // special case device created playlists 3110afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) { 3111c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST); 3112c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood // create a file path for the benefit of MTP 31139be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood path = mExternalStoragePaths[0] 3114afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "/Playlists/" + values.getAsString(Audio.Playlists.NAME); 3115c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(MediaStore.MediaColumns.DATA, path); 311610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen values.put(FileColumns.PARENT, getParent(helper, db, path)); 3117c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } else { 311863b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang Log.e(TAG, "path is empty in insertFile()"); 3119c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3120c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } else { 3121c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood format = MediaFile.getFormatCode(path, mimeType); 3122c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3123c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3124c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (format != 0) { 3125c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.FORMAT, format); 3126c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (mimeType == null) { 3127c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood mimeType = MediaFile.getMimeTypeForFormatCode(format); 3128c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3129c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3130c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 3131801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood if (mimeType == null && path != null) { 3132c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood mimeType = MediaFile.getMimeTypeForFile(path); 3133c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3134c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (mimeType != null) { 3135c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.MIME_TYPE, mimeType); 3136afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3137f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) { 3138afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int fileType = MediaFile.getFileTypeForMimeType(mimeType); 3139afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (MediaFile.isAudioFileType(fileType)) { 3140afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_AUDIO; 3141afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isVideoFileType(fileType)) { 3142afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_VIDEO; 3143afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isImageFileType(fileType)) { 3144afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_IMAGE; 3145afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isPlayListFileType(fileType)) { 3146afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_PLAYLIST; 3147afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3148afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3149c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3150afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put(FileColumns.MEDIA_TYPE, mediaType); 3151c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 3152afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (rowId == 0) { 3153afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) { 31543572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen String name = values.getAsString(Audio.Playlists.NAME); 3155282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood if (name == null && path == null) { 3156282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood // MediaScanner will compute the name from the path if we have one 3157afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood throw new IllegalArgumentException( 3158282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood "no name was provided when inserting abstract playlist"); 3159afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3160afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 3161a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu if (path == null) { 3162afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // path might be null for playlists created on the device 3163afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // or transfered via MTP 3164afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood throw new IllegalArgumentException( 3165afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "no path was provided when inserting new file"); 3166afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 3167e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3168b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 3169f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood // make sure modification date and size are set 3170f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (path != null) { 3171f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood File file = new File(path); 3172f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (file.exists()) { 3173f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000); 317434d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen if (!values.containsKey(FileColumns.SIZE)) { 317534d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen values.put(FileColumns.SIZE, file.length()); 317634d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen } 317720368a558593c685623bfff6df1f4545d53f8291Jia Su // make sure date taken time is set 317820368a558593c685623bfff6df1f4545d53f8291Jia Su if (mediaType == FileColumns.MEDIA_TYPE_IMAGE 317920368a558593c685623bfff6df1f4545d53f8291Jia Su || mediaType == FileColumns.MEDIA_TYPE_VIDEO) { 318020368a558593c685623bfff6df1f4545d53f8291Jia Su computeTakenTime(values); 318120368a558593c685623bfff6df1f4545d53f8291Jia Su } 3182e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 31835d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 3184b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 3185afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long parent = values.getAsLong(FileColumns.PARENT); 31865d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood if (parent == null) { 3187e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (path != null) { 318810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long parentId = getParent(helper, db, path); 318916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood values.put(FileColumns.PARENT, parentId); 3190e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 31919be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood } 31929be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood Integer storage = values.getAsInteger(FileColumns.STORAGE_ID); 31939be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood if (storage == null) { 31949be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood int storageId = getStorageId(path); 31959be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood values.put(FileColumns.STORAGE_ID, storageId); 31965d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 3197b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 319810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3199afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = db.insert("files", FileColumns.DATE_MODIFIED, values); 3200afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId); 3201afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 320210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (rowId != -1 && notify) { 32032dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke notifyRowIds.add(rowId); 3204afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 32055d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } else { 320610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 320716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.update("files", values, FileColumns._ID + "=?", 3208afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood new String[] { Long.toString(rowId) }); 32095d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 3210d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen if (format == MtpConstants.FORMAT_ASSOCIATION) { 3211d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen mDirectoryCache.put(path, rowId); 3212d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen } 3213afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 3214afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood return rowId; 32151717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood } 32161717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 321710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) { 321810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3219a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen Cursor c = db.query("files", sMediaTableColumns, "_id=?", 3220e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Integer.toString(handle) }, 3221e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 3222e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 3223e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 3224afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long playlistId = c.getLong(0); 3225afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 3226afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) { 3227e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only support object references for playlist objects 3228e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return null; 3229e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 323010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3231e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return db.rawQuery(OBJECT_REFERENCES_QUERY, 3232afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood new String[] { Long.toString(playlistId) } ); 3233e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3234e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 3235e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 3236e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 3237e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3238e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3239e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return null; 3240e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3241e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 324210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db, 324310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen int handle, ContentValues values[]) { 3244e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // first look up the media table and media ID for the object 3245e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long playlistId = 0; 324610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3247a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen Cursor c = db.query("files", sMediaTableColumns, "_id=?", 3248e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Integer.toString(handle) }, 3249e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 3250e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 3251e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 3252afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 3253afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) { 3254e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only support object references for playlist objects 3255e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return 0; 3256e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3257afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood playlistId = c.getLong(0); 3258e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3259e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 3260e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 3261e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 3262e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3263e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3264e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (playlistId == 0) { 3265e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return 0; 3266e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3267e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 3268e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // next delete any existing entries 326910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumDeletes++; 327010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen db.delete("audio_playlists_map", "playlist_id=?", 3271e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Long.toString(playlistId) }); 3272e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 3273e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // finally add the new entries 3274e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int count = values.length; 3275e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int added = 0; 3276e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues[] valuesList = new ContentValues[count]; 3277e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood for (int i = 0; i < count; i++) { 3278e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // convert object ID to audio ID 3279e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long audioId = 0; 3280e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID); 328110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 3282a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen c = db.query("files", sMediaTableColumns, "_id=?", 3283e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Long.toString(objectId) }, 3284e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 3285e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 3286e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 3287afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 328850d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) { 3289e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only allow audio files in playlists, so skip 3290e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood continue; 3291e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3292afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood audioId = c.getLong(0); 3293e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3294e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 3295e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 3296e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 3297e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3298e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3299e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (audioId != 0) { 3300e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues v = new ContentValues(); 3301e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId); 3302e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId); 3303a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added); 3304a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood valuesList[added++] = v; 3305e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3306e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3307e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (added < count) { 3308e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we weren't able to find everything on the list, so lets resize the array 3309e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // and pass what we have. 3310e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues[] newValues = new ContentValues[added]; 3311e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood System.arraycopy(valuesList, 0, newValues, 0, added); 3312e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood valuesList = newValues; 3313e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3314e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return playlistBulkInsert(db, 3315e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId), 3316e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood valuesList); 3317e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 3318e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 3319b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood private static final String[] GENRE_LOOKUP_PROJECTION = new String[] { 3320b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Audio.Genres._ID, // 0 3321b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Audio.Genres.NAME, // 1 3322b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood }; 3323b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 3324b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood private void updateGenre(long rowId, String genre) { 3325b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Uri uri = null; 3326b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Cursor cursor = null; 3327b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Uri genresUri = MediaStore.Audio.Genres.getContentUri("external"); 3328b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood try { 3329b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // see if the genre already exists 3330b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?", 3331b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood new String[] { genre }, null); 3332b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (cursor == null || cursor.getCount() == 0) { 3333b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // genre does not exist, so create the genre in the genre table 3334b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood ContentValues values = new ContentValues(); 3335b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood values.put(MediaStore.Audio.Genres.NAME, genre); 3336b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood uri = insert(genresUri, values); 3337b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } else { 3338b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // genre already exists, so compute its Uri 3339b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood cursor.moveToNext(); 3340b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0)); 3341b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3342b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (uri != null) { 3343b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY); 3344b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3345b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } finally { 3346b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // release the cursor if it exists 3347b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (cursor != null) { 3348b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood cursor.close(); 3349b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3350b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3351b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 3352b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (uri != null) { 3353b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // add entry to audio_genre_map 3354b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood ContentValues values = new ContentValues(); 3355b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId)); 3356b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood insert(uri, values); 3357b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3358b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3359b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 33602dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke private Uri insertInternal(Uri uri, int match, ContentValues initialValues, 33612dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke ArrayList<Long> notifyRowIds) { 3362b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey final String volumeName = getVolumeName(uri); 3363b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 3364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long rowId; 3365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3366d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues); 3367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 3368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == MEDIA_SCANNER) { 3369bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME); 337010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper database = getDatabaseForUri( 337110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Uri.parse("content://media/" + mMediaScannerVolume + "/audio")); 337210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (database == null) { 337310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume); 337410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } else { 337510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mScanStartTime = SystemClock.currentTimeMicro(); 337610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 3377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return MediaStore.getMediaScannerUri(); 3378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3380b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood String genre = null; 338138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen String path = null; 3382b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (initialValues != null) { 3383b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood genre = initialValues.getAsString(Audio.AudioColumns.GENRE); 3384b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood initialValues.remove(Audio.AudioColumns.GENRE); 338538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen path = initialValues.getAsString(MediaStore.MediaColumns.DATA); 3386b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3387b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 338838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri newUri = null; 339010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 339110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null && match != VOLUMES && match != MTP_CONNECTED) { 3392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 3394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 339510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen 3396819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null 339710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen : helper.getWritableDatabase()); 3398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: { 34012dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, initialValues, 34022dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds); 3403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3404b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey MediaDocumentsProvider.onMediaStoreInsert( 3405b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey getContext(), volumeName, FileColumns.MEDIA_TYPE_IMAGE, rowId); 3406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId( 3407b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey Images.Media.getContentUri(volumeName), rowId); 3408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3412b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // This will be triggered by requestMediaThumbnail (see getThumbnailUri) 3413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS: { 341410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg", 3415b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "DCIM/.thumbnails"); 341610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("thumbnails", "name", values); 3418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Images.Thumbnails. 3420b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey getContentUri(volumeName), rowId); 3421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3425b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri) 3426b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: { 342710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg", 3428b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "DCIM/.thumbnails"); 342910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3430b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen rowId = db.insert("videothumbnails", "name", values); 3431b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (rowId > 0) { 3432b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen newUri = ContentUris.withAppendedId(Video.Thumbnails. 3433b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey getContentUri(volumeName), rowId); 3434b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3435b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 3436b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3437b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 3438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: { 34392dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, initialValues, 34402dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds); 3441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3442b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey MediaDocumentsProvider.onMediaStoreInsert( 3443b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey getContext(), volumeName, FileColumns.MEDIA_TYPE_AUDIO, rowId); 3444b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey newUri = ContentUris.withAppendedId( 3445b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey Audio.Media.getContentUri(volumeName), rowId); 3446b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (genre != null) { 3447b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood updateGenre(rowId, genre); 3448b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 3449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: { 3454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long audioId = Long.parseLong(uri.getPathSegments().get(2)); 3455bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Genres.Members.AUDIO_ID, audioId); 345710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3458ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen rowId = db.insert("audio_genres_map", "genre_id", values); 3459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: { 3466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long audioId = Long.parseLong(uri.getPathSegments().get(2)); 3467bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Playlists.Members.AUDIO_ID, audioId); 346910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("audio_playlists_map", "playlist_id", 3471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values); 3472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: { 347910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3480bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood rowId = db.insert("audio_genres", "audio_id", initialValues); 3481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3482b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey newUri = ContentUris.withAppendedId( 3483b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey Audio.Genres.getContentUri(volumeName), rowId); 3484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: { 3489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long genreId = Long.parseLong(uri.getPathSegments().get(3)); 3490bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Genres.Members.GENRE_ID, genreId); 349210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("audio_genres_map", "genre_id", values); 3494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: { 3501bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000); 35032dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, values, 35042dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds); 3505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3506b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey newUri = ContentUris.withAppendedId( 3507b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey Audio.Playlists.getContentUri(volumeName), rowId); 3508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 3513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: { 3514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long playlistId = Long.parseLong(uri.getPathSegments().get(3)); 3515bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = new ContentValues(initialValues); 3516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId); 351710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3518ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen rowId = db.insert("audio_playlists_map", "playlist_id", values); 3519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: { 35262dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke rowId = insertFile(helper, uri, initialValues, 35272dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds); 3528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3529b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey MediaDocumentsProvider.onMediaStoreInsert( 3530b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey getContext(), volumeName, FileColumns.MEDIA_TYPE_VIDEO, rowId); 3531b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey newUri = ContentUris.withAppendedId( 3532b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey Video.Media.getContentUri(volumeName), rowId); 3533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3537c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood case AUDIO_ALBUMART: { 353810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper.mInternal) { 3539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("no internal album art allowed"); 3540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3541bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood ContentValues values = null; 3542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 3543bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER); 3544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (IllegalStateException ex) { 3545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // probably no more room to store albumthumbs 3546bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood values = initialValues; 3547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 354810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 3549801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values); 3550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 3552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3554c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 3555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VOLUMES: 35575619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen { 35585619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen String name = initialValues.getAsString("name"); 35595619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen Uri attachedVolume = attachVolume(name); 35605619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) { 35615619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen DatabaseHelper dbhelper = getDatabaseForUri(attachedVolume); 35625619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen if (dbhelper == null) { 35635619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen Log.e(TAG, "no database for attached volume " + attachedVolume); 35645619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } else { 35655619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen dbhelper.mScanStartTime = SystemClock.currentTimeMicro(); 35665619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } 35675619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } 35685619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen return attachedVolume; 35695619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen } 3570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3571819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood case MTP_CONNECTED: 357234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 357334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService == null) { 357434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Context context = getContext(); 357534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // MTP is connected, so grab a connection to MtpService 357634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood context.bindService(new Intent(context, MtpService.class), 357734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpServiceConnection, Context.BIND_AUTO_CREATE); 357834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 357934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 3580819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood break; 3581819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood 3582afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FILES: 358310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen rowId = insertFile(helper, uri, initialValues, 35842dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds); 3585fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood if (rowId > 0) { 3586b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey newUri = Files.getContentUri(volumeName, rowId); 3587fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood } 3588fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood break; 3589fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood 3590e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 35912dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke // We don't send a notification if the insert originated from MTP 359210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen rowId = insertFile(helper, uri, initialValues, 35932dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds); 3594afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (rowId > 0) { 3595b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey newUri = Files.getMtpObjectsUri(volumeName, rowId); 35965d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 35975d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood break; 35985d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood 3599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("Invalid URI " + uri); 3601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 360338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) { 360438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen // need to set the media_type of all the files below this folder to 0 360538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processNewNoMediaPath(helper, db, path); 360638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 3607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return newUri; 3608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 361038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen /* 361138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * Sets the media type of all files below the newly added .nomedia file or 361238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * hidden folder to 0, so the entries no longer appear in e.g. the audio and 361338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * images views. 361438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * 361538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * @param path The path to the new .nomedia file or hidden directory 361638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen */ 36175461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen private void processNewNoMediaPath(final DatabaseHelper helper, final SQLiteDatabase db, 36185461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen final String path) { 36195461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen final File nomedia = new File(path); 362038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (nomedia.exists()) { 36215461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen hidePath(helper, db, path); 36225461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } else { 36235461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen // File doesn't exist. Try again in a little while. 36245461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen // XXX there's probably a better way of doing this 36255461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen new Thread(new Runnable() { 36265461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen @Override 36275461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen public void run() { 36285461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen SystemClock.sleep(2000); 36295461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen if (nomedia.exists()) { 36305461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen hidePath(helper, db, path); 36315461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } else { 36325461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen Log.w(TAG, "does not exist: " + path, new Exception()); 36335461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } 36345461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen }}).start(); 363538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 363638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 363738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 36385461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen private void hidePath(DatabaseHelper helper, SQLiteDatabase db, String path) { 36395461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen File nomedia = new File(path); 36405461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent(); 36415461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen ContentValues mediatype = new ContentValues(); 36425461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen mediatype.put("media_type", 0); 36435461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen int numrows = db.update("files", mediatype, 3644a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync "_data >= ? AND _data < ?", 3645a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang new String[] { hiddenroot + "/", hiddenroot + "0"}); 36465461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen helper.mNumUpdates += numrows; 36475461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen ContentResolver res = getContext().getContentResolver(); 36485461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen res.notifyChange(Uri.parse("content://media/"), null); 36495461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen } 36505461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen 365138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen /* 365238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * Rescan files for missing metadata and set their type accordingly. 365338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * There is code for detecting the removal of a nomedia file or renaming of 365438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * a directory from hidden to non-hidden in the MediaScanner and MtpDatabase, 365538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen * both of which call here. 365638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen */ 365738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen private void processRemovedNoMediaPath(final String path) { 365838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen final DatabaseHelper helper; 365938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (path.startsWith(mExternalStoragePaths[0])) { 366038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen helper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI); 366138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } else { 366238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen helper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI); 366338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 366438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 366538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen new ScannerClient(getContext(), db, path); 366638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 366738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 366838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen private static final class ScannerClient implements MediaScannerConnectionClient { 366938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen String mPath = null; 367038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen MediaScannerConnection mScannerConnection; 367138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen SQLiteDatabase mDb; 367238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 367338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public ScannerClient(Context context, SQLiteDatabase db, String path) { 367438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mDb = db; 367538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mPath = path; 367638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection = new MediaScannerConnection(context, this); 367738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection.connect(); 367838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 367938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 368038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen @Override 368138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public void onMediaScannerConnected() { 36825461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen Cursor c = mDb.query("files", openFileColumns, 3683a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync "_data >= ? AND _data < ?", 3684a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang new String[] { mPath + "/", mPath + "0"}, 36855461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen null, null, null); 368638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen while (c.moveToNext()) { 368738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen String d = c.getString(0); 368838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen File f = new File(d); 368938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (f.isFile()) { 369038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection.scanFile(d, null); 369138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 369238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 369338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen mScannerConnection.disconnect(); 369438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen c.close(); 369538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 369638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 369738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen @Override 369838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public void onScanCompleted(String path, Uri uri) { 369938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 370038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 370138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 3702cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen @Override 3703cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 3704cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen throws OperationApplicationException { 3705cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 3706cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // The operations array provides no overall information about the URI(s) being operated 3707cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // on, so begin a transaction for ALL of the databases. 3708cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI); 3709cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI); 3710cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen SQLiteDatabase idb = ihelper.getWritableDatabase(); 3711cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.beginTransaction(); 3712cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen SQLiteDatabase edb = null; 3713cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (ehelper != null) { 3714cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb = ehelper.getWritableDatabase(); 3715cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.beginTransaction(); 3716cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3717cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen try { 3718cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen ContentProviderResult[] result = super.applyBatch(operations); 3719cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.setTransactionSuccessful(); 3720cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (edb != null) { 3721cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.setTransactionSuccessful(); 3722cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3723cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // Rather than sending targeted change notifications for every Uri 3724cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // affected by the batch operation, just invalidate the entire internal 3725cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // and external name space. 3726cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen ContentResolver res = getContext().getContentResolver(); 3727cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen res.notifyChange(Uri.parse("content://media/"), null); 3728cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen return result; 3729cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } finally { 3730cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.endTransaction(); 3731cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (edb != null) { 3732cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.endTransaction(); 3733cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3734cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3735cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 3736cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 3737cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 37389299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) { 3739b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mMediaThumbQueue) { 3740e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen MediaThumbRequest req = null; 3741e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen try { 3742e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen req = new MediaThumbRequest( 37439299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen getContext().getContentResolver(), path, uri, priority, magic); 3744e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen mMediaThumbQueue.add(req); 3745e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen // Trigger the handler. 3746e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB); 3747e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen msg.sendToTarget(); 3748e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } catch (Throwable t) { 3749e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Log.w(TAG, t); 3750e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } 3751b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return req; 3752b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3753b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3754b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 3755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String generateFileName(boolean internal, String preferredExtension, String directoryName) 3756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 3757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create a random file 3758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name = String.valueOf(System.currentTimeMillis()); 3759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (internal) { 3761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("Writing to internal storage is not supported."); 3762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project// return Environment.getDataDirectory() 3763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project// + "/" + directoryName + "/" + name + preferredExtension; 3764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 37659be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension; 3766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private boolean ensureFileExists(String path) { 3770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(path); 3771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (file.exists()) { 3772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return true; 3773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we will not attempt to create the first directory in the path 3775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // (for example, do not create /sdcard if the SD card is not mounted) 3776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int secondSlash = path.indexOf('/', 1); 3777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (secondSlash < 1) return false; 3778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String directoryPath = path.substring(0, secondSlash); 3779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File directory = new File(directoryPath); 3780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!directory.exists()) 3781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return false; 378217ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood file.getParentFile().mkdirs(); 3783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 378417ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood return file.createNewFile(); 3785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch(IOException ioe) { 3786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "File creation failed", ioe); 3787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return false; 3789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final class GetTableAndWhereOutParameter { 3793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String table; 3794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String where; 3795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final GetTableAndWhereOutParameter sGetTableAndWhereParam = 3798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new GetTableAndWhereOutParameter(); 3799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private void getTableAndWhere(Uri uri, int match, String userWhere, 3801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project GetTableAndWhereOutParameter out) { 3802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String where = null; 3803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 38049f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin case IMAGES_MEDIA: 3805afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3806afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE; 38079f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin break; 38089f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin 3809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 3810afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id = " + uri.getPathSegments().get(3); 3812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3814b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS_ID: 3815b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen where = "_id=" + uri.getPathSegments().get(3); 3816b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS: 3817b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen out.table = "thumbnails"; 3818b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 3819b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 3820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 3821afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3822afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO; 3823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 3826afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 3831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3); 3833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 3836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3) + 3838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND genre_id=" + uri.getPathSegments().get(5); 3839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 3842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists"; 3843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3); 3844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 3847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists"; 3848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3) + 3849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND playlists_id=" + uri.getPathSegments().get(5); 3850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 3853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 3857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 3862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "genre_id=" + uri.getPathSegments().get(3); 3864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 3867afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3868afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST; 3869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 3872afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 3877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists_map"; 3878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "playlist_id=" + uri.getPathSegments().get(3); 3879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 3882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists_map"; 3883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "playlist_id=" + uri.getPathSegments().get(3) + 3884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND _id=" + uri.getPathSegments().get(5); 3885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMART_ID: 3888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "album_art"; 3889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "album_id=" + uri.getPathSegments().get(3); 3890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 3893afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3894afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO; 3895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 3898afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3902b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS_ID: 3903b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen where = "_id=" + uri.getPathSegments().get(3); 3904b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: 3905b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen out.table = "videothumbnails"; 3906b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 3907b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 390816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES_ID: 3909e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 39101717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood where = "_id=" + uri.getPathSegments().get(2); 391116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES: 3912e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 391316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood out.table = "files"; 39141717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood break; 39151717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 3916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown or unsupported URL: " + uri.toString()); 3919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Add in the user requested WHERE clause, if needed 3922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!TextUtils.isEmpty(userWhere)) { 3923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!TextUtils.isEmpty(where)) { 3924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = where + " AND (" + userWhere + ")"; 3925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = userWhere; 3927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = where; 3930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 3934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int delete(Uri uri, String userWhere, String[] whereArgs) { 393501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen uri = safeUncanonicalize(uri); 3936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count; 3937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 3938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 3940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == MEDIA_SCANNER) { 3941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mMediaScannerVolume == null) { 3942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return 0; 3943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 394410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper database = getDatabaseForUri( 394510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Uri.parse("content://media/" + mMediaScannerVolume + "/audio")); 394610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (database == null) { 394710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume); 394810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } else { 394910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mScanStopTime = SystemClock.currentTimeMicro(); 3950988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen String msg = dump(database, false); 3951988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen logToDb(database.getWritableDatabase(), msg); 395210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 3953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mMediaScannerVolume = null; 3954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return 1; 3955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3957819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood if (match == VOLUMES_ID) { 3958819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood detachVolume(uri); 3959819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood count = 1; 3960819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood } else if (match == MTP_CONNECTED) { 396134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 396234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 396334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // MTP has disconnected, so release our connection to MtpService 396434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood getContext().unbindService(mMtpServiceConnection); 396534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood count = 1; 396634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // mMtpServiceConnection.onServiceDisconnected might not get called, 396734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // so set mMtpService = null here 396834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 396934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } else { 397034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood count = 0; 397134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 397234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 3973819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood } else { 3974b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey final String volumeName = getVolumeName(uri); 3975b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 3976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 3977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 3978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3979819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood "Unknown URI: " + uri + " match: " + match); 3980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 398110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 3982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getWritableDatabase(); 3983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (sGetTableAndWhereParam) { 3985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam); 3986a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen if (sGetTableAndWhereParam.table.equals("files")) { 3987d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA); 3988d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen if (deleteparam == null || ! deleteparam.equals("false")) { 3989d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen database.mNumQueries++; 3990d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen Cursor c = db.query(sGetTableAndWhereParam.table, 3991d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen sMediaTypeDataId, 3992d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen sGetTableAndWhereParam.where, whereArgs, null, null, null); 3993d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen String [] idvalue = new String[] { "" }; 39944eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen String [] playlistvalues = new String[] { "", "" }; 3995d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen while (c.moveToNext()) { 3996b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey final int mediaType = c.getInt(0); 3997b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey final String data = c.getString(1); 3998b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey final long id = c.getLong(2); 3999b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 4000b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey if (mediaType == FileColumns.MEDIA_TYPE_IMAGE) { 4001b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey deleteIfAllowed(uri, data); 4002b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName, 4003b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey FileColumns.MEDIA_TYPE_IMAGE, id); 4004b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 4005b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey idvalue[0] = String.valueOf(id); 4006b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen database.mNumQueries++; 4007b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen Cursor cc = db.query("thumbnails", sDataOnlyColumn, 4008b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen "image_id=?", idvalue, null, null, null); 4009b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen while (cc.moveToNext()) { 4010b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen deleteIfAllowed(uri, cc.getString(0)); 4011a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 4012b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen cc.close(); 4013b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen database.mNumDeletes++; 4014b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen db.delete("thumbnails", "image_id=?", idvalue); 4015b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 4016b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey } else if (mediaType == FileColumns.MEDIA_TYPE_VIDEO) { 4017b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey deleteIfAllowed(uri, data); 4018b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName, 4019b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey FileColumns.MEDIA_TYPE_VIDEO, id); 4020b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 4021b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey } else if (mediaType == FileColumns.MEDIA_TYPE_AUDIO) { 4022f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen if (!database.mInternal) { 4023b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey MediaDocumentsProvider.onMediaStoreDelete(getContext(), 4024b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey volumeName, FileColumns.MEDIA_TYPE_AUDIO, id); 4025b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 4026b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey idvalue[0] = String.valueOf(id); 40279b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumDeletes += 2; // also count the one below 4028f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen db.delete("audio_genres_map", "audio_id=?", idvalue); 40294eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen // for each playlist that the item appears in, move 40304eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen // all the items behind it forward by one 40314eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Cursor cc = db.query("audio_playlists_map", 40324eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen sPlaylistIdPlayOrder, 40334eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen "audio_id=?", idvalue, null, null, null); 40344eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen while (cc.moveToNext()) { 40354eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen playlistvalues[0] = "" + cc.getLong(0); 40364eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen playlistvalues[1] = "" + cc.getInt(1); 40379b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen database.mNumUpdates++; 40384eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen db.execSQL("UPDATE audio_playlists_map" + 40394eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " SET play_order=play_order-1" + 40404eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE playlist_id=? AND play_order>?", 40414eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen playlistvalues); 40424eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen } 40434eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen cc.close(); 4044f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen db.delete("audio_playlists_map", "audio_id=?", idvalue); 4045f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen } 4046b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey } else if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) { 4047d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen // TODO, maybe: remove the audio_playlists_cleanup trigger and implement 4048d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen // it functionality here (clean up the playlist map) 40495afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen } 4050a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 4051d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen c.close(); 4052a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 4053a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen } 4054a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen 4055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 405636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood case MTP_OBJECTS: 4057e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 4058d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood try { 4059d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // don't send objectRemoved event since this originated from MTP 4060d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood mDisableMtpObjectCallbacks = true; 406110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 406210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen count = db.delete("files", sGetTableAndWhereParam.where, whereArgs); 4063d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } finally { 4064d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood mDisableMtpObjectCallbacks = false; 4065d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 406636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood break; 406778b2885edc406273d688536b0eadfea006b20662Marco Nelissen case AUDIO_GENRES_ID_MEMBERS: 406810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 406978b2885edc406273d688536b0eadfea006b20662Marco Nelissen count = db.delete("audio_genres_map", 407078b2885edc406273d688536b0eadfea006b20662Marco Nelissen sGetTableAndWhereParam.where, whereArgs); 407178b2885edc406273d688536b0eadfea006b20662Marco Nelissen break; 4072166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen 4073a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen case IMAGES_THUMBNAILS_ID: 4074a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen case IMAGES_THUMBNAILS: 4075166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen case VIDEO_THUMBNAILS_ID: 4076166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen case VIDEO_THUMBNAILS: 4077166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen // Delete the referenced files first. 4078166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen Cursor c = db.query(sGetTableAndWhereParam.table, 4079166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen sDataOnlyColumn, 4080166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen sGetTableAndWhereParam.where, whereArgs, null, null, null); 4081166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen if (c != null) { 4082166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen while (c.moveToNext()) { 4083b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen deleteIfAllowed(uri, c.getString(0)); 4084166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } 4085166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen c.close(); 4086166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen } 4087166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen database.mNumDeletes++; 4088166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen count = db.delete(sGetTableAndWhereParam.table, 4089166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen sGetTableAndWhereParam.where, whereArgs); 4090166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen break; 4091166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen 4092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 409310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen database.mNumDeletes++; 4094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.delete(sGetTableAndWhereParam.table, 4095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 4096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4098b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 40993631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // Since there are multiple Uris that can refer to the same files 41003631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // and deletes can affect other objects in storage (like subdirectories 41013631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // or playlists) we will notify a change on the entire volume to make 41023631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // sure no listeners miss the notification. 4103b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volumeName); 41043631d46b679a64a16918698121916b60d7c86e97Mike Lockwood getContext().getContentResolver().notifyChange(notifyUri, null); 4105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return count; 4109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 411238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen public Bundle call(String method, String arg, Bundle extras) { 411338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (MediaStore.UNHIDE_CALL.equals(method)) { 411438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processRemovedNoMediaPath(arg); 411538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen return null; 411638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 411738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen throw new UnsupportedOperationException("Unsupported call: " + method); 411838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 411938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen 412038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen @Override 4121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int update(Uri uri, ContentValues initialValues, String userWhere, 4122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] whereArgs) { 412301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen uri = safeUncanonicalize(uri); 4124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count; 4125b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues); 4126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 412710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper = getDatabaseForUri(uri); 412810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (helper == null) { 4129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 4130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 4131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 413210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 413310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen 413410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen SQLiteDatabase db = helper.getWritableDatabase(); 4135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4136b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood String genre = null; 4137b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (initialValues != null) { 4138b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood genre = initialValues.getAsString(Audio.AudioColumns.GENRE); 4139b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood initialValues.remove(Audio.AudioColumns.GENRE); 4140b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 4141b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood 4142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (sGetTableAndWhereParam) { 4143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam); 4144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 41451d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // special case renaming directories via MTP. 41461d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // in this case we must update all paths in the database with 41471d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // the directory name as a prefix 41481d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID) 41491d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood && initialValues != null && initialValues.size() == 1) { 41501d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood String oldPath = null; 4151801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA); 41527f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.remove(newPath); 41531d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // MtpDatabase will rename the directory first, so we test the new file name 415438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen File f = new File(newPath); 415538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (newPath != null && f.isDirectory()) { 415610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 41571d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION, 41581d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood userWhere, whereArgs, null, null, null); 41591d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood try { 41601d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (cursor != null && cursor.moveToNext()) { 41611d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood oldPath = cursor.getString(1); 41621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 41631d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } finally { 41641d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (cursor != null) cursor.close(); 41651d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 41661d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (oldPath != null) { 41677f36494e085c26c69cd5925e54028822025eff29Marco Nelissen mDirectoryCache.remove(oldPath); 41681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // first rename the row for the directory 416910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 41701d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood count = db.update(sGetTableAndWhereParam.table, initialValues, 41711d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood sGetTableAndWhereParam.where, whereArgs); 41721d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (count > 0) { 41739bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood // update the paths of any files and folders contained in the directory 41743fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen Object[] bindArgs = new Object[] { 41753fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen newPath, 41763fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen oldPath.length() + 1, 41773fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen oldPath + "/", 41783fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen oldPath + "0", 41799bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood // update bucket_display_name and bucket_id based on new path 41809bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood f.getName(), 41819bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood f.toString().toLowerCase().hashCode() 41829bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood }; 418310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 41845461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen db.execSQL("UPDATE files SET _data=?1||SUBSTR(_data, ?2)" + 41859bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood // also update bucket_display_name 41863fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen ",bucket_display_name=?5" + 41873fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen ",bucket_id=?6" + 4188a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync " WHERE _data >= ?3 AND _data < ?4;", 41895461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen bindArgs); 41901d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 41911d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 41921d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (count > 0 && !db.inTransaction()) { 41931d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood getContext().getContentResolver().notifyChange(uri, null); 41941d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 419538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen if (f.getName().startsWith(".")) { 419638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen // the new directory name is hidden 419738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processNewNoMediaPath(helper, db, newPath); 419838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } 41991d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood return count; 42001d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 420138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) { 420238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen processNewNoMediaPath(helper, db, newPath); 42031d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 42041d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 42051d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 4206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 4207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 4208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 4209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 4210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 42112658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST); 42122658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION); 42132658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen values.remove(MediaStore.Audio.Media.COMPILATION); 421407656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen 4215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Insert the artist into the artist table and remove it from 4216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // the input values 4217a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String artist = values.getAsString("artist"); 42186006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen values.remove("artist"); 4219a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (artist != null) { 4220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long artistRowId; 422110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> artistCache = helper.mArtistCache; 4222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized(artistCache) { 4223a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen Long temp = artistCache.get(artist); 4224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (temp == null) { 422510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen artistRowId = getKeyIdForName(helper, db, 422610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "artists", "artist_key", "artist", 4227acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen artist, artist, null, 0, null, artistCache, uri); 4228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 4229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project artistRowId = temp.longValue(); 4230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("artist_id", Integer.toString((int)artistRowId)); 4233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 423559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // Do the same for the album field. 4236a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String so = values.getAsString("album"); 42376006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen values.remove("album"); 4238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (so != null) { 4239801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood String path = values.getAsString(MediaStore.MediaColumns.DATA); 424059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen int albumHash = 0; 42412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (albumartist != null) { 42422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumHash = albumartist.hashCode(); 42432658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else if (compilation != null && compilation.equals("1")) { 42442658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // nothing to do, hash already set 424559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } else { 42469289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (path == null) { 42479289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (match == AUDIO_MEDIA) { 42489289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen Log.w(TAG, "Possible multi row album name update without" 42499289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen + " path could give wrong album key"); 42509289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } else { 42519289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen //Log.w(TAG, "Specify path to avoid extra query"); 42529289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen Cursor c = query(uri, 42539289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen new String[] { MediaStore.Audio.Media.DATA}, 42549289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen null, null, null); 42559289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (c != null) { 42569289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen try { 42579289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen int numrows = c.getCount(); 42589289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (numrows == 1) { 42599289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen c.moveToFirst(); 42609289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen path = c.getString(0); 42619289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } else { 42629289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen Log.e(TAG, "" + numrows + " rows for " + uri); 42639289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 42649289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } finally { 42659289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen c.close(); 42669289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 42679289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 42689289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 42699289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 42709289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen if (path != null) { 42719289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen albumHash = path.substring(0, path.lastIndexOf('/')).hashCode(); 42729289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen } 427359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 42742658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 4275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = so.toString(); 4276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long albumRowId; 427710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen HashMap<String, Long> albumCache = helper.mAlbumCache; 4278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized(albumCache) { 427959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen String cacheName = s + albumHash; 428059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen Long temp = albumCache.get(cacheName); 4281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (temp == null) { 428210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen albumRowId = getKeyIdForName(helper, db, 428310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen "albums", "album_key", "album", 4284a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen s, cacheName, path, albumHash, artist, albumCache, uri); 4285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 4286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project albumRowId = temp.longValue(); 4287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("album_id", Integer.toString((int)albumRowId)); 4290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // don't allow the title_key field to be updated directly 4293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove("title_key"); 4294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // If the title field is modified, update the title_key 4295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project so = values.getAsString("title"); 4296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (so != null) { 4297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = so.toString(); 4298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("title_key", MediaStore.Audio.keyFor(s)); 4299e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen // do a final trim of the title, in case it started with the special 4300e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen // "sort first" character (ascii \001) 4301e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen values.remove("title"); 4302e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen values.put("title", s.trim()); 4303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 430510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 4306afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood count = db.update(sGetTableAndWhereParam.table, values, 4307afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood sGetTableAndWhereParam.where, whereArgs); 4308b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (genre != null) { 4309b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood if (count == 1 && match == AUDIO_MEDIA_ID) { 4310b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood long rowId = Long.parseLong(uri.getPathSegments().get(3)); 4311b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood updateGenre(rowId, genre); 4312b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } else { 4313b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood // can't handle genres for bulk update or for non-audio files 4314b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood Log.w(TAG, "ignoring genre in update: count = " 4315b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood + count + " match = " + match); 4316b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 4317b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood } 4318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 4321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 4322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 4323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 4324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 4325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 4326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Don't allow bucket id or display name to be updated directly. 4327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // The same names are used for both images and table columns, so 4328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we use the ImageColumns constants here. 4329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove(ImageColumns.BUCKET_ID); 4330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove(ImageColumns.BUCKET_DISPLAY_NAME); 4331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // If the data is being modified update the bucket values 4332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = values.getAsString(MediaColumns.DATA); 4333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (data != null) { 4334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project computeBucketValues(data, values); 4335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4336b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen computeTakenTime(values); 433710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 4338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.update(sGetTableAndWhereParam.table, values, 4339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 434001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // if this is a request from MediaScanner, DATA should contains file path 434101a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // we only process update request from media scanner, otherwise the requests 434201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // could be duplicate. 434301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) { 434410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 434501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen Cursor c = db.query(sGetTableAndWhereParam.table, 434601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen READY_FLAG_PROJECTION, sGetTableAndWhereParam.where, 434701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen whereArgs, null, null, null); 4348b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c != null) { 4349216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen try { 4350216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen while (c.moveToNext()) { 4351216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen long magic = c.getLong(2); 4352216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen if (magic == 0) { 4353216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen requestMediaThumbnail(c.getString(1), uri, 4354216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen MediaThumbRequest.PRIORITY_NORMAL, 0); 4355216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } 4356b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4357216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } finally { 4358216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c.close(); 4359b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4360b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4361b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4364f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen 4365f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 4366f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen String moveit = uri.getQueryParameter("move"); 4367f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (moveit != null) { 4368f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER; 4369f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (initialValues.containsKey(key)) { 4370f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen int newpos = initialValues.getAsInteger(key); 4371f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen List <String> segments = uri.getPathSegments(); 4372f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen long playlist = Long.valueOf(segments.get(3)); 4373f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen int oldpos = Integer.valueOf(segments.get(5)); 437410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen return movePlaylistEntry(helper, db, playlist, oldpos, newpos); 4375f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4376f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen throw new IllegalArgumentException("Need to specify " + key + 4377f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " when using 'move' parameter"); 4378f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4379f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // fall through 4380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 438110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 4382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.update(sGetTableAndWhereParam.table, initialValues, 4383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 4384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 4385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4387cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // in a transaction, the code that began the transaction should be taking 4388cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // care of notifications once it ends the transaction successfully 4389cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (count > 0 && !db.inTransaction()) { 4390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 4391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return count; 4393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 439510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db, 439610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen long playlist, int from, int to) { 4397f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (from == to) { 4398f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen return 0; 4399f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4400f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.beginTransaction(); 44015e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé int numlines = 0; 4402f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen try { 440310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates += 3; 44044eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen Cursor c = db.query("audio_playlists_map", 44054eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen new String [] {"play_order" }, 44064eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order", 44074eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen from + ",1"); 44084eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.moveToFirst(); 44094eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen int from_play_order = c.getInt(0); 44104eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.close(); 44114eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c = db.query("audio_playlists_map", 44124eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen new String [] {"play_order" }, 44134eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order", 44144eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen to + ",1"); 44154eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.moveToFirst(); 44164eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen int to_play_order = c.getInt(0); 44174eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen c.close(); 4418f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=-1" + 44194eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE play_order=" + from_play_order + 4420f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 4421f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // We could just run both of the next two statements, but only one of 4422f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // of them will actually do anything, so might as well skip the compile 4423f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // and execute steps. 4424f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (from < to) { 4425f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" + 44264eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE play_order<=" + to_play_order + 44274eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " AND play_order>" + from_play_order + 4428f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 4429f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen numlines = to - from + 1; 4430f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } else { 4431f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" + 44324eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " WHERE play_order>=" + to_play_order + 44334eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen " AND play_order<" + from_play_order + 4434f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 4435f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen numlines = from - to + 1; 4436f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 44374eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=" + to_play_order + 4438f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " WHERE play_order=-1 AND playlist_id=" + playlist); 4439f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.setTransactionSuccessful(); 4440f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } finally { 4441f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.endTransaction(); 4442f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 44435e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé 44445e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI 44455e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé .buildUpon().appendEncodedPath(String.valueOf(playlist)).build(); 44465e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé // notifyChange() must be called after the database transaction is ended 44475e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé // or the listeners will read the old data in the callback 44485e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé getContext().getContentResolver().notifyChange(uri, null); 44495e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé 44505e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé return numlines; 4451f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 4452f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen 4453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String[] openFileColumns = new String[] { 4454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns.DATA, 4455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 4456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 4458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public ParcelFileDescriptor openFile(Uri uri, String mode) 4459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throws FileNotFoundException { 446071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 446101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen uri = safeUncanonicalize(uri); 4462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor pfd = null; 446371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 446471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) { 446571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // get album art for the specified media file 446671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen DatabaseHelper database = getDatabaseForUri(uri); 446771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (database == null) { 446871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw new IllegalStateException("Couldn't open database for " + uri); 446971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 447071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen SQLiteDatabase db = database.getReadableDatabase(); 44715fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 44725fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 44735fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 447471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 447571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen int songid = Integer.parseInt(uri.getPathSegments().get(3)); 447671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.setTables("audio_meta"); 447771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.appendWhere("_id=" + songid); 447871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen Cursor c = qb.query(db, 447971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen new String [] { 448071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen MediaStore.Audio.Media.DATA, 448171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen MediaStore.Audio.Media.ALBUM_ID }, 448271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen null, null, null, null, null); 448371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (c.moveToFirst()) { 448471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen String audiopath = c.getString(0); 448571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen int albumid = c.getInt(1); 448671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // Try to get existing album art for this album first, which 448771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // could possibly have been obtained from a different file. 448871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // If that fails, try to get it from this specific file. 448971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid); 449071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen try { 4491007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey pfd = openFileAndEnforcePathPermissionsHelper(newUri, mode); 449271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } catch (FileNotFoundException ex) { 449371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // That didn't work, now try to get it from the specific file 449434fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen pfd = getThumb(database, db, audiopath, albumid, null); 449571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 449671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 449771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen c.close(); 449871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return pfd; 449971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 450071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 4501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 4502007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey pfd = openFileAndEnforcePathPermissionsHelper(uri, mode); 4503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (FileNotFoundException ex) { 450471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (mode.contains("w")) { 450571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // if the file couldn't be created, we shouldn't extract album art 450671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw ex; 450771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 450871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 4509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) { 4510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Tried to open an album art file which does not exist. Regenerate. 4511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 4512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 4513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw ex; 4514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getReadableDatabase(); 45165fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 45175fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 45185fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 4519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 4520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int albumid = Integer.parseInt(uri.getPathSegments().get(3)); 452171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.setTables("audio_meta"); 4522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("album_id=" + albumid); 4523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = qb.query(db, 4524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new String [] { 4525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Media.DATA }, 4526a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen null, null, null, null, MediaStore.Audio.Media.TRACK); 452771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (c.moveToFirst()) { 4528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String audiopath = c.getString(0); 452934fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen pfd = getThumb(database, db, audiopath, albumid, uri); 4530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project c.close(); 4532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 453371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (pfd == null) { 453471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw ex; 453571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 4536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return pfd; 4538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4540007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** 4541007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey * Return the {@link MediaColumns#DATA} field for the given {@code Uri}. 4542007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey */ 4543007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private File queryForDataFile(Uri uri) throws FileNotFoundException { 4544007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey final Cursor cursor = query( 4545007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey uri, new String[] { MediaColumns.DATA }, null, null, null); 45468ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey if (cursor == null) { 45478ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey throw new FileNotFoundException("Missing cursor for " + uri); 45488ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey } 45498ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey 4550007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey try { 4551007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey switch (cursor.getCount()) { 4552007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey case 0: 4553007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new FileNotFoundException("No entry for " + uri); 4554007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey case 1: 4555007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey if (cursor.moveToFirst()) { 4556007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey return new File(cursor.getString(0)); 4557007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } else { 4558007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new FileNotFoundException("Unable to read entry for " + uri); 4559007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4560007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey default: 4561007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new FileNotFoundException("Multiple items at " + uri); 4562007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4563007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } finally { 4564007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey cursor.close(); 4565007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4566007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4567007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4568007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey /** 4569007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey * Replacement for {@link #openFileHelper(Uri, String)} which enforces any 4570007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey * permissions applicable to the path before returning. 4571007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey */ 4572007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode) 4573007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throws FileNotFoundException { 4574a2bd8dfeac57dc09727c6d16ebf9bac1061df23dAdam Lesinski final int modeBits = ParcelFileDescriptor.parseMode(mode); 4575007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4576f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey File file = queryForDataFile(uri); 4577b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen 4578b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen checkAccess(uri, file, modeBits); 4579b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen 4580b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen // Bypass emulation layer when file is opened for reading, but only 4581b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen // when opening read-only and we have an exact match. 4582b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen if (modeBits == MODE_READ_ONLY) { 4583b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen file = Environment.maybeTranslateEmulatedPathToInternal(file); 4584b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen } 4585b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen 4586b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen return ParcelFileDescriptor.open(file, modeBits); 4587b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen } 4588b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen 4589b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen private void deleteIfAllowed(Uri uri, String path) { 4590b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen try { 4591b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen File file = new File(path); 4592b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen checkAccess(uri, file, ParcelFileDescriptor.MODE_WRITE_ONLY); 4593b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen file.delete(); 4594b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen } catch (Exception e) { 4595b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen Log.e(TAG, "Couldn't delete " + path); 4596b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen } 4597b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen } 4598b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen 4599b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen private void checkAccess(Uri uri, File file, int modeBits) throws FileNotFoundException { 4600b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0; 4601007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey final String path; 4602007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey try { 4603007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey path = file.getCanonicalPath(); 4604007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } catch (IOException e) { 4605007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey throw new IllegalArgumentException("Unable to resolve canonical path for " + file, e); 4606007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4607007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 46086ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen Context c = getContext(); 46096ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen boolean readGranted = 46106ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) 46116ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen == PackageManager.PERMISSION_GRANTED); 46126ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen 46135943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen if (path.startsWith(sExternalPath) || path.startsWith(sLegacyPath)) { 46146ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen if (!readGranted) { 46153e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen c.enforceCallingOrSelfPermission( 46163e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen READ_EXTERNAL_STORAGE, "External path: " + path); 46173e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen } 4618007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4619007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey if (isWrite) { 46203e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 46213e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen != PackageManager.PERMISSION_GRANTED) { 46223e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen c.enforceCallingOrSelfPermission( 46233e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen WRITE_EXTERNAL_STORAGE, "External path: " + path); 46243e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen } 4625007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4626f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey 4627007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } else if (path.startsWith(sCachePath)) { 46286ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen if (!readGranted) { 46296ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen c.enforceCallingOrSelfPermission( 46306ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen ACCESS_CACHE_FILESYSTEM, "Cache path: " + path); 46316ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen } 463270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } else if (isWrite) { 463370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // don't write to non-cache, non-sdcard files. 463470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + file); 4635dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen } else if (isSecondaryExternalPath(path)) { 4636dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen // read access is OK with the appropriate permission 46376ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen if (!readGranted) { 46386ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen c.enforceCallingOrSelfPermission( 46396ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen READ_EXTERNAL_STORAGE, "External path: " + path); 46406ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen } 464170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } else { 464270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen checkWorldReadAccess(path); 4643007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4644007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey } 4645007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey 4646dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen private boolean isSecondaryExternalPath(String path) { 4647dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen for (int i = mExternalStoragePaths.length - 1; i >= 0; --i) { 4648dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen if (path.startsWith(mExternalStoragePaths[i])) { 4649dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen return true; 4650dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen } 4651dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen } 4652dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen return false; 4653dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen } 4654dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen 465570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen /** 465670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen * Check whether the path is a world-readable file 465770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen */ 465870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen private void checkWorldReadAccess(String path) throws FileNotFoundException { 465970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 466070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen try { 466170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen StructStat stat = Libcore.os.stat(path); 466270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen int accessBits = OsConstants.S_IROTH; 466370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen if (OsConstants.S_ISREG(stat.st_mode) && 466470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen ((stat.st_mode & accessBits) == accessBits)) { 466570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen checkLeadingPathComponentsWorldExecutable(path); 466670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen return; 466770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 466870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } catch (ErrnoException e) { 466970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // couldn't stat the file, either it doesn't exist or isn't 467070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // accessible to us 467170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 467270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 467370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + path); 467470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 467570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 467670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen private void checkLeadingPathComponentsWorldExecutable(String filePath) 467770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throws FileNotFoundException { 467870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen File parent = new File(filePath).getParentFile(); 467970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 468070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen int accessBits = OsConstants.S_IXOTH; 468170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 468270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen while (parent != null) { 468370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen if (! parent.exists()) { 468470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // parent dir doesn't exist, give up 468570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("access denied"); 468670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 468770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen try { 468870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen StructStat stat = Libcore.os.stat(parent.getPath()); 468970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen if ((stat.st_mode & accessBits) != accessBits) { 469070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // the parent dir doesn't have the appropriate access 469170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + filePath); 469270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 469370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } catch (ErrnoException e1) { 469470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen // couldn't stat() parent 469570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen throw new FileNotFoundException("Can't access " + filePath); 469670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 469770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen parent = parent.getParentFile(); 469870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 469970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen } 470070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen 4701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private class ThumbData { 470210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper; 4703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db; 4704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String path; 4705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long album_id; 4706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri albumart_uri; 4707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 470910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db, 471010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen String path, long album_id) { 47118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber synchronized (mPendingThumbs) { 47128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (mPendingThumbs.contains(path)) { 47138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // There's already a request to make an album art thumbnail 47148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // for this audio file in the queue. 47158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return; 47168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber mPendingThumbs.add(path); 47198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 4721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ThumbData d = new ThumbData(); 472210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen d.helper = helper; 4723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.db = db; 4724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.path = path; 4725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.album_id = album_id; 4726a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id); 47278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Instead of processing thumbnail requests in the order they were 47298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // received we instead process them stack-based, i.e. LIFO. 47308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // The idea behind this is that the most recently requested thumbnails 47318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // are most likely the ones still in the user's view, whereas those 47328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // requested earlier may have already scrolled off. 47338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber synchronized (mThumbRequestStack) { 47348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber mThumbRequestStack.push(d); 47358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 47368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 47378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Trigger the handler. 4738b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB); 4739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project msg.sendToTarget(); 4740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4742f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen //Return true if the artPath is the dir as it in mExternalStoragePaths 4743f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen //for multi storage support 4744f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen private static boolean isRootStorageDir(String artPath) { 4745f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen for ( int i = 0; i < mExternalStoragePaths.length; i++) { 4746f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen if ((mExternalStoragePaths[i] != null) && 4747f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen (artPath.equalsIgnoreCase(mExternalStoragePaths[i]))) 4748f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen return true; 4749f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen } 4750f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen return false; 4751f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen } 4752f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen 47538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Extract compressed image data from the audio file itself or, if that fails, 47548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // look for a file "AlbumArt.jpg" in the containing directory. 47558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private static byte[] getCompressedAlbumArt(Context context, String path) { 47568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber byte[] compressed = null; 4757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 4759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File f = new File(path); 4760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f, 4761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor.MODE_READ_ONLY); 4762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 47638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber MediaScanner scanner = new MediaScanner(context); 47648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber compressed = scanner.extractAlbumArt(pfd.getFileDescriptor()); 4765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project pfd.close(); 4766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4767d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // If no embedded art exists, look for a suitable image file in the 47683f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen // same directory as the media file, except if that directory is 47693f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen // is the root directory of the sd card or the download directory. 4770d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // We look for, in order of preference: 4771d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 0 AlbumArt.jpg 4772d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 1 AlbumArt*Large.jpg 4773d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 2 Any other jpg image with 'albumart' anywhere in the name 4774d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 3 Any other jpg image 4775d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 4 any other png image 47768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (compressed == null && path != null) { 4777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int lastSlash = path.lastIndexOf('/'); 4778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lastSlash > 0) { 4779d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 47803f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen String artPath = path.substring(0, lastSlash); 47810fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen String dwndir = Environment.getExternalStoragePublicDirectory( 47822f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); 4783d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 4784d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String bestmatch = null; 4785d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen synchronized (sFolderArtMap) { 4786d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (sFolderArtMap.containsKey(artPath)) { 4787d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = sFolderArtMap.get(artPath); 4788f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen } else if (!isRootStorageDir(artPath) && 4789ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen !artPath.equalsIgnoreCase(dwndir)) { 4790d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen File dir = new File(artPath); 4791d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String [] entrynames = dir.list(); 4792d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (entrynames == null) { 4793d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen return null; 4794d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4795d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = null; 4796d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen int matchlevel = 1000; 4797d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen for (int i = entrynames.length - 1; i >=0; i--) { 4798d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String entry = entrynames[i].toLowerCase(); 4799d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (entry.equals("albumart.jpg")) { 4800d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4801d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen break; 4802d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.startsWith("albumart") 4803d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && entry.endsWith("large.jpg") 4804d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && matchlevel > 1) { 4805d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4806d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 1; 4807d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.contains("albumart") 4808d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && entry.endsWith(".jpg") 4809d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && matchlevel > 2) { 4810d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4811d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 2; 4812d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.endsWith(".jpg") && matchlevel > 3) { 4813d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4814d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 3; 4815d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.endsWith(".png") && matchlevel > 4) { 4816d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 4817d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 4; 4818d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4819d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4820d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // note that this may insert null if no album art was found 4821d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen sFolderArtMap.put(artPath, bestmatch); 4822d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4823d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4824d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 4825d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (bestmatch != null) { 48263f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen File file = new File(artPath, bestmatch); 4827d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (file.exists()) { 4828d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen FileInputStream stream = null; 4829d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen try { 48302c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen compressed = new byte[(int)file.length()]; 4831d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream = new FileInputStream(file); 4832d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream.read(compressed); 4833d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } catch (IOException ex) { 4834d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen compressed = null; 48352c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen } catch (OutOfMemoryError ex) { 48362c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen Log.w(TAG, ex); 48372c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen compressed = null; 4838d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } finally { 4839d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (stream != null) { 4840d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream.close(); 4841d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 4842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 48478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (IOException e) { 48488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 4849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 48508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return compressed; 48518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 4852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 48538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Return a URI to write the album art to and update the database as necessary. 485410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) { 48558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Uri out = null; 48568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // TODO: this could be done more efficiently with a call to db.replace(), which 48578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // replaces or inserts as needed, making it unnecessary to query() first. 48588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (albumart_uri != null) { 4859801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA }, 48608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber null, null, null); 4861d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson try { 4862d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (c != null && c.moveToFirst()) { 4863d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson String albumart_path = c.getString(0); 4864d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (ensureFileExists(albumart_path)) { 4865d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson out = albumart_uri; 4866d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } 4867d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } else { 4868d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson albumart_uri = null; 4869d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } 4870d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } finally { 4871d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (c != null) { 4872d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson c.close(); 4873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 487571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 487671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (albumart_uri == null){ 48778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber ContentValues initialValues = new ContentValues(); 48788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber initialValues.put("album_id", album_id); 48798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 48808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER); 488110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 4882801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values); 48838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (rowId > 0) { 48848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber out = ContentUris.withAppendedId(ALBUMART_URI, rowId); 488550c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen // ensure the parent directory exists 488650c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen String albumart_path = values.getAsString(MediaStore.MediaColumns.DATA); 488750c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen ensureFileExists(albumart_path); 4888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 48898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (IllegalStateException ex) { 48908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Log.e(TAG, "error creating album thumb file"); 48918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 48928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 48938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return out; 48948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 48958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 48968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Write out the album art to the output URI, recompresses the given Bitmap 48978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // if necessary, otherwise writes the compressed data. 48988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private void writeAlbumArt( 489920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) throws IOException { 490020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen OutputStream outstream = null; 49018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 490220e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen outstream = getContext().getContentResolver().openOutputStream(out); 49038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 49048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (!need_to_recompress) { 49058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // No need to recompress here, just write out the original 49068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // compressed data here. 49078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber outstream.write(compressed); 49088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } else { 490920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen if (!bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream)) { 491020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen throw new IOException("failed to compress bitmap"); 491120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } 4912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 491320e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } finally { 491420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen IoUtils.closeQuietly(outstream); 4915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 49168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 49178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 491834fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen private ParcelFileDescriptor getThumb(DatabaseHelper helper, SQLiteDatabase db, String path, 491934fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen long album_id, Uri albumart_uri) { 492071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen ThumbData d = new ThumbData(); 492134fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen d.helper = helper; 492271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.db = db; 492371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.path = path; 492471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.album_id = album_id; 492571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.albumart_uri = albumart_uri; 492671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return makeThumbInternal(d); 492771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 492871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 492971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen private ParcelFileDescriptor makeThumbInternal(ThumbData d) { 49308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber byte[] compressed = getCompressedAlbumArt(getContext(), d.path); 4931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 49328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (compressed == null) { 493371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 49348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 49358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 49368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Bitmap bm = null; 49378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber boolean need_to_recompress = true; 49388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 49398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 49408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // get the size of the bitmap 49418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber BitmapFactory.Options opts = new BitmapFactory.Options(); 49428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inJustDecodeBounds = true; 49438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inSampleSize = 1; 49448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts); 49458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 49468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // request a reasonably sized output image 494770676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann final Resources r = getContext().getResources(); 494870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size); 494970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) { 49508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.outHeight /= 2; 49518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.outWidth /= 2; 49528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inSampleSize *= 2; 49538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 49548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 49558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (opts.inSampleSize == 1) { 49568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // The original album art was of proper size, we won't have to 49578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // recompress the bitmap later. 49588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber need_to_recompress = false; 49598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } else { 49608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // get the image for real now 49618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inJustDecodeBounds = false; 49628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inPreferredConfig = Bitmap.Config.RGB_565; 49638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts); 49648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 49658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (bm != null && bm.getConfig() == null) { 4966a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false); 4967a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (nbm != null && nbm != bm) { 4968a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm.recycle(); 4969a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm = nbm; 4970a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } 49718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 49728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 49738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (Exception e) { 49748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 49758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 49768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (need_to_recompress && bm == null) { 497771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 49788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 49798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 498071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (d.albumart_uri == null) { 498171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // this one doesn't need to be saved (probably a song with an unknown album), 498271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // so stick it in a memory file and return that 498371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen try { 498424001394f571b1f0378840cbf299288e4df10508Bjorn Bringert return ParcelFileDescriptor.fromData(compressed, "albumthumb"); 498571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } catch (IOException e) { 498671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 498771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } else { 4988a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // This one needs to actually be saved on the sd card. 4989a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // This is wrapped in a transaction because there are various things 4990a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // that could go wrong while generating the thumbnail, and we only want 4991a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // to update the database when all steps succeeded. 4992a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.beginTransaction(); 499320e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen Uri out = null; 499420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen ParcelFileDescriptor pfd = null; 4995a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen try { 499620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri); 4997a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen 4998a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (out != null) { 4999a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen writeAlbumArt(need_to_recompress, out, compressed, bm); 5000a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen getContext().getContentResolver().notifyChange(MEDIA_URI, null); 500120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen pfd = openFileHelper(out, "r"); 5002a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.setTransactionSuccessful(); 5003a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen return pfd; 5004a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } 500520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } catch (IOException ex) { 5006a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // do nothing, just return null below 5007a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } catch (UnsupportedOperationException ex) { 5008a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // do nothing, just return null below 5009a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } finally { 5010a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.endTransaction(); 5011a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (bm != null) { 5012a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm.recycle(); 501371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 501420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen if (pfd == null && out != null) { 501520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // Thumbnail was not written successfully, delete the entry that refers to it. 501620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // Note that this only does something if getAlbumArtOutputUri() reused an 501720e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // existing entry from the database. If a new entry was created, it will 501820e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen // have been rolled back as part of backing out the transaction. 501920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen getContext().getContentResolver().delete(out, null, null); 502020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen } 502171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 50228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 502371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 5024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 5027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Look up the artist or album entry for the given name, creating that entry 5028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * if it does not already exists. 5029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db The database 5030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param table The table to store the key/name pair in. 5031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param keyField The name of the key-column 5032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param nameField The name of the name-column 5033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param rawName The name that the calling app was trying to insert into the database 503459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param cacheName The string that will be inserted in to the cache 503559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param path The full path to the file being inserted in to the audio table 503659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param albumHash A hash to distinguish between different albums of the same name 5037a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen * @param artist The name of the artist, if known 5038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param cache The cache to add this entry to 5039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param srcuri The Uri that prompted the call to this method, used for determining whether this is 5040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * the internal or external database 5041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return The row ID for this artist/album, or -1 if the provided name was invalid 5042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 504310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db, 504410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen String table, String keyField, String nameField, 504559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen String rawName, String cacheName, String path, int albumHash, 5046a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String artist, HashMap<String, Long> cache, Uri srcuri) { 5047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long rowId; 5048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rawName == null || rawName.length() == 0) { 505051cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen rawName = MediaStore.UNKNOWN_STRING; 5051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String k = MediaStore.Audio.keyFor(rawName); 5053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (k == null) { 505551cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen // shouldn't happen, since we only get null keys for null inputs 505651cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen Log.e(TAG, "null key", new Exception()); 5057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return -1; 5058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 506059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen boolean isAlbum = table.equals("albums"); 5061e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName); 506259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 50632658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // To distinguish same-named albums, we append a hash. The hash is based 50642658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // on the "album artist" tag if present, otherwise on the "compilation" tag 50652658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // if present, otherwise on the path. 506659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // Ideally we would also take things like CDDB ID in to account, so 506759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // we can group files from the same album that aren't in the same 506859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // folder, but this is a quick and easy start that works immediately 506959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // without requiring support from the mp3, mp4 and Ogg meta data 507059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // readers, as long as the albums are in different folders. 5071a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (isAlbum) { 507259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen k = k + albumHash; 5073a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (isUnknown) { 5074a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen k = k + artist; 5075a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen } 507659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 507759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 5078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String [] selargs = { k }; 507910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumQueries++; 5080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null); 5081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 5083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (c.getCount()) { 5084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case 0: { 5085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // insert new entry into table 5086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues otherValues = new ContentValues(); 5087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project otherValues.put(keyField, k); 5088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project otherValues.put(nameField, rawName); 508910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumInserts++; 5090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert(table, "duration", otherValues); 509159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (path != null && isAlbum && ! isUnknown) { 5092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // We just inserted a new album. Now create an album art thumbnail for it. 509310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen makeThumbAsync(helper, db, path, rowId); 5094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 5096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = srcuri.toString().substring(16, 24); // extract internal/external 5097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId); 5098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 5099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 5102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case 1: { 5103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Use the existing entry 5104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project c.moveToFirst(); 5105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = c.getLong(0); 5106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Determine whether the current rawName is better than what's 5108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // currently stored in the table, and update the table if it is. 5109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String currentFancyName = c.getString(2); 5110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String bestName = makeBestName(rawName, currentFancyName); 5111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!bestName.equals(currentFancyName)) { 5112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // update the table with the new name 5113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues newValues = new ContentValues(); 5114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newValues.put(nameField, bestName); 511510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper.mNumUpdates++; 5116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null); 5117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = srcuri.toString().substring(16, 24); // extract internal/external 5118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId); 5119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 5120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 5123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 5124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // corrupt database 5125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "Multiple entries in table " + table + " for key " + k); 5126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = -1; 5127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 5128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 5130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (c != null) c.close(); 5131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 513359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (cache != null && ! isUnknown) { 513459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen cache.put(cacheName, rowId); 5135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return rowId; 5137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 5140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Returns the best string to use for display, given two names. 5141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Note that this function does not necessarily return either one 5142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * of the provided names; it may decide to return a better alternative 5143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * (for example, specifying the inputs "Police" and "Police, The" will 5144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * return "The Police") 5145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * The basic assumptions are: 5147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - longer is better ("The police" is better than "Police") 5148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - prefix is better ("The Police" is better than "Police, The") 5149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - accents are better ("Motörhead" is better than "Motorhead") 5150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param one The first of the two names to consider 5152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param two The last of the two names to consider 5153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return The actual name to use 5154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 5155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String makeBestName(String one, String two) { 5156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name; 5157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Longer names are usually better. 5159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (one.length() > two.length()) { 5160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = one; 5161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 5162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Names with accents are usually better, and conveniently sort later 5163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) { 5164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = one; 5165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 5166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = two; 5167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Prefixes are better than postfixes. 5171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (name.endsWith(", the") || name.endsWith(",the") || 5172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name.endsWith(", an") || name.endsWith(",an") || 5173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name.endsWith(", a") || name.endsWith(",a")) { 5174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String fix = name.substring(1 + name.lastIndexOf(',')); 5175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = fix.trim() + " " + name.substring(0, name.lastIndexOf(',')); 5176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // TODO: word-capitalize the resulting name 5179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return name; 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 * Looks up the database based on the given URI. 5185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param uri The requested URI 5187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @returns the database for the given URI 5188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 5189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private DatabaseHelper getDatabaseForUri(Uri uri) { 5190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 51915619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen if (uri.getPathSegments().size() >= 1) { 5192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return mDatabases.get(uri.getPathSegments().get(0)); 5193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 5196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5198fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn static boolean isMediaDatabaseName(String name) { 5199fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (INTERNAL_DATABASE_NAME.equals(name)) { 5200fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5201fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5202fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (EXTERNAL_DATABASE_NAME.equals(name)) { 5203fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5204fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5205168d49e39a356cdbd0fa04c356e3480e413d6f34kwangjung.kim if (name.startsWith("external-") && name.endsWith(".db")) { 5206fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5207fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5208fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return false; 5209fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5210fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn 5211fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn static boolean isInternalMediaDatabaseName(String name) { 5212fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn if (INTERNAL_DATABASE_NAME.equals(name)) { 5213fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return true; 5214fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5215fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn return false; 5216fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn } 5217fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn 5218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 5219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Attach the database for a volume (internal or external). 5220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Does nothing if the volume is already attached, otherwise 5221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * checks the volume ID and sets up the corresponding database. 5222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}. 5224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return the content URI of the attached volume. 5225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 5226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Uri attachVolume(String volume) { 522775392afde5217038b0c98077758bb9329c2431b2Jeff Brown if (Binder.getCallingPid() != Process.myPid()) { 5228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new SecurityException( 5229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Opening and closing databases not allowed."); 5230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 5233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mDatabases.get(volume) != null) { // Already attached 5234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Uri.parse("content://media/" + volume); 5235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5237993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Context context = getContext(); 523810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen DatabaseHelper helper; 5239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_VOLUME.equals(volume)) { 524010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true, 5241fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn false, mObjectRemovedCallback); 5242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (EXTERNAL_VOLUME.equals(volume)) { 5243993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (Environment.isExternalStorageRemovable()) { 52445d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey final StorageVolume actualVolume = mStorageManager.getPrimaryVolume(); 52455d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey final int volumeId = actualVolume.getFatVolumeId(); 5246993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood 5247ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // Must check for failure! 5248ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // If the volume is not (yet) mounted, this will create a new 5249ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // external-ffffffff.db database instead of the one we expect. Then, if 5250ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // android.process.media is later killed and respawned, the real external 5251ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // database will be attached, containing stale records, or worse, be empty. 52525d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey if (volumeId == -1) { 5253ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick String state = Environment.getExternalStorageState(); 5254ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick if (Environment.MEDIA_MOUNTED.equals(state) || 5255ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { 5256ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // This may happen if external storage was _just_ mounted. It may also 5257ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // happen if the volume ID is _actually_ 0xffffffff, in which case it 5258ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // must be changed since FileUtils::getFatVolumeId doesn't allow for 5259ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // that. It may also indicate that FileUtils::getFatVolumeId is broken 5260ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick // (missing ioctl), which is also impossible to disambiguate. 5261ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick Log.e(TAG, "Can't obtain external volume ID even though it's mounted."); 5262ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick } else { 5263ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick Log.i(TAG, "External volume is not (yet) mounted, cannot attach."); 5264ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick } 5265ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick 5266ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick throw new IllegalArgumentException("Can't obtain external volume ID for " + 5267ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick volume + " volume."); 5268ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick } 5269ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick 5270993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // generate database name based on volume ID 52715d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey String dbName = "external-" + Integer.toHexString(volumeId) + ".db"; 527210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper = new DatabaseHelper(context, dbName, false, 5273fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn false, mObjectRemovedCallback); 52745d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey mVolumeId = volumeId; 5275993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 5276993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // external database name should be EXTERNAL_DATABASE_NAME 5277993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // however earlier releases used the external-XXXXXXXX.db naming 5278993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // for devices without removable storage, and in that case we need to convert 5279993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // to this new convention 5280993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME); 5281993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (!dbFile.exists()) { 5282993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // find the most recent external database and rename it to 5283993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // EXTERNAL_DATABASE_NAME, and delete any other older 5284993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // external database files 5285993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File recentDbFile = null; 5286993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood for (String database : context.databaseList()) { 5287ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim if (database.startsWith("external-") && database.endsWith(".db")) { 5288993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File file = context.getDatabasePath(database); 5289993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile == null) { 5290993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood recentDbFile = file; 5291993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else if (file.lastModified() > recentDbFile.lastModified()) { 5292ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim context.deleteDatabase(recentDbFile.getName()); 5293993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood recentDbFile = file; 5294993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 5295ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim context.deleteDatabase(file.getName()); 5296993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5297993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5298993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5299993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile != null) { 5300993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile.renameTo(dbFile)) { 5301993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Log.d(TAG, "renamed database " + recentDbFile.getName() + 5302993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood " to " + EXTERNAL_DATABASE_NAME); 5303993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 5304993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Log.e(TAG, "Failed to rename database " + recentDbFile.getName() + 5305993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood " to " + EXTERNAL_DATABASE_NAME); 5306993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // This shouldn't happen, but if it does, continue using 5307993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // the file under its old name 5308993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood dbFile = recentDbFile; 5309993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5310993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5311993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME 5312993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 531310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen helper = new DatabaseHelper(context, dbFile.getName(), false, 5314fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn false, mObjectRemovedCallback); 5315993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 5316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 5317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException("There is no volume named " + volume); 5318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 532010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen mDatabases.put(volume, helper); 5321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 532210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (!helper.mInternal) { 5323ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // create default directories (only happens on first boot) 532410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen createDefaultFolders(helper, helper.getWritableDatabase()); 5325ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 5326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // clean up stray album art files: delete every file not in the database 53279be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles(); 5328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashSet<String> fileSet = new HashSet(); 5329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; files != null && i < files.length; i++) { 5330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fileSet.add(files[i].getPath()); 5331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, 5334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null); 5335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 5336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor != null && cursor.moveToNext()) { 5337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fileSet.remove(cursor.getString(0)); 5338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 5340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (cursor != null) cursor.close(); 5341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Iterator<String> iterator = fileSet.iterator(); 5344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (iterator.hasNext()) { 5345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String filename = iterator.next(); 5346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename); 5347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new File(filename).delete(); 5348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume); 5353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Uri.parse("content://media/" + volume); 5354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 5357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Detach the database for a volume (must be external). 5358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Does nothing if the volume is already detached, otherwise 5359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * closes the database and sends a notification to listeners. 5360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 5361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param uri The content URI of the volume, as returned by {@link #attachVolume} 5362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 5363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private void detachVolume(Uri uri) { 536475392afde5217038b0c98077758bb9329c2431b2Jeff Brown if (Binder.getCallingPid() != Process.myPid()) { 5365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new SecurityException( 5366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Opening and closing databases not allowed."); 5367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = uri.getPathSegments().get(0); 5370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_VOLUME.equals(volume)) { 5371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 5372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Deleting the internal volume is not allowed"); 5373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (!EXTERNAL_VOLUME.equals(volume)) { 5374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException( 5375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "There is no volume named " + volume); 5376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 5379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = mDatabases.get(volume); 5380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) return; 5381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 5383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // touch the database file to show it is most recently used 5384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(database.getReadableDatabase().getPath()); 5385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file.setLastModified(System.currentTimeMillis()); 5386e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood } catch (Exception e) { 5387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "Can't touch database file", e); 5388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mDatabases.remove(volume); 5391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project database.close(); 5392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 5395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume); 5396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 5397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static String TAG = "MediaProvider"; 5399ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer private static final boolean LOCAL_LOGV = false; 5400971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn 5401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String INTERNAL_DATABASE_NAME = "internal.db"; 5402993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood private static final String EXTERNAL_DATABASE_NAME = "external.db"; 5403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // maximum number of cached external databases to keep 5405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int MAX_EXTERNAL_DATABASES = 3; 5406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Delete databases that have not been used in two months 5408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60) 5409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final long OBSOLETE_DATABASE_DB = 5184000000L; 5410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private HashMap<String, DatabaseHelper> mDatabases; 5412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Handler mThumbHandler; 5414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // name of the volume currently being scanned by the media scanner (or null) 5416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String mMediaScannerVolume; 5417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 54180027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // current FAT volume ID 5419993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood private int mVolumeId = -1; 54200027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 5421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final String INTERNAL_VOLUME = "internal"; 5422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final String EXTERNAL_VOLUME = "external"; 5423268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs"; 5424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // path for writing contents of in memory temp database 5426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String mTempDatabasePath; 5427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 54281717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS 542916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood // are stored in the "files" table, so do not renumber them unless you also add 54301717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood // a corresponding database upgrade step for it. 5431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_MEDIA = 1; 5432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_MEDIA_ID = 2; 5433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_THUMBNAILS = 3; 5434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_THUMBNAILS_ID = 4; 5435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA = 100; 5437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID = 101; 5438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_GENRES = 102; 5439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_GENRES_ID = 103; 5440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104; 5441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105; 5442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES = 106; 5443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES_ID = 107; 5444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES_ID_MEMBERS = 108; 5445bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen private static final int AUDIO_GENRES_ALL_MEMBERS = 109; 5446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS = 110; 5447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID = 111; 5448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112; 5449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113; 5450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS = 114; 5451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS_ID = 115; 5452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMS = 116; 5453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMS_ID = 117; 5454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS_ID_ALBUMS = 118; 5455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMART = 119; 5456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMART_ID = 120; 545771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen private static final int AUDIO_ALBUMART_FILE_ID = 121; 5458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VIDEO_MEDIA = 200; 5460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VIDEO_MEDIA_ID = 201; 5461b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int VIDEO_THUMBNAILS = 202; 5462b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int VIDEO_THUMBNAILS_ID = 203; 5463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VOLUMES = 300; 5465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VOLUMES_ID = 301; 5466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5467a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_LEGACY = 400; 5468a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_BASIC = 401; 5469a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_FANCY = 402; 5470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int MEDIA_SCANNER = 500; 5472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 54730027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen private static final int FS_ID = 600; 5474704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen private static final int VERSION = 601; 54750027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 547616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood private static final int FILES = 700; 547716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood private static final int FILES_ID = 701; 5478a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu 5479e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood // Used only by the MTP implementation 5480e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECTS = 702; 5481e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECTS_ID = 703; 5482e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECT_REFERENCES = 704; 5483819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood // UsbReceiver calls insert() and delete() with this URI to tell us 5484819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood // when MTP is connected and disconnected 5485819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood private static final int MTP_CONNECTED = 705; 5486b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 5487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final UriMatcher URI_MATCHER = 5488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new UriMatcher(UriMatcher.NO_MATCH); 5489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5490b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final String[] ID_PROJECTION = new String[] { 5491b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns._ID 5492b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen }; 5493b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 54941d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood private static final String[] PATH_PROJECTION = new String[] { 54951d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood MediaStore.MediaColumns._ID, 54961d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood MediaStore.MediaColumns.DATA, 54971d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood }; 54981d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 5499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String[] MIME_TYPE_PROJECTION = new String[] { 5500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns._ID, // 0 5501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns.MIME_TYPE, // 1 5502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 5503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5504b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final String[] READY_FLAG_PROJECTION = new String[] { 5505b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns._ID, 5506b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns.DATA, 5507b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Images.Media.MINI_THUMB_MAGIC 5508b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen }; 5509b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 5510e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood private static final String OBJECT_REFERENCES_QUERY = 5511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map" 5512afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?" 5513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER; 5514e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 5515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static 5516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 5517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA); 5518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID); 5519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS); 5520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID); 5521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA); 5523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID); 5524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES); 5525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID); 5526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS); 5527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID); 5528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES); 5529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID); 5530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS); 5531bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS); 5532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS); 5533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID); 5534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS); 5535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID); 5536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS); 5537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID); 5538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS); 5539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS); 5540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID); 5541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART); 5542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID); 554371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID); 5544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA); 5546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID); 5547b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS); 5548b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID); 5549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER); 5551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 55520027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen URI_MATCHER.addURI("media", "*/fs_id", FS_ID); 5553704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen URI_MATCHER.addURI("media", "*/version", VERSION); 55540027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 5555819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED); 5556819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood 5557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*", VOLUMES_ID); 5558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", null, VOLUMES); 5559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 5560b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // Used by MTP implementation 556116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood URI_MATCHER.addURI("media", "*/file", FILES); 556216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood URI_MATCHER.addURI("media", "*/file/#", FILES_ID); 5563e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS); 5564e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID); 5565e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES); 5566b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 5567a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen /** 5568a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen * @deprecated use the 'basic' or 'fancy' search Uris instead 5569a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen */ 5570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY, 5571a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_LEGACY); 5572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*", 5573a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_LEGACY); 5574a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 5575a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // used for search suggestions 5576a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY, 5577a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_BASIC); 5578a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY + 5579a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "/*", AUDIO_SEARCH_BASIC); 5580a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 5581a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // used by the music app's search activity 5582a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY); 5583a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY); 5584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 558510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen 5586b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey private static String getVolumeName(Uri uri) { 5587b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey final List<String> segments = uri.getPathSegments(); 5588b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey if (segments != null && segments.size() > 0) { 5589b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey return segments.get(0); 5590b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey } else { 5591b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey return null; 5592b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey } 5593b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey } 5594b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey 559510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen @Override 559610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 559710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen Collection<DatabaseHelper> foo = mDatabases.values(); 559810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen for (DatabaseHelper dbh: foo) { 5599988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen writer.println(dump(dbh, true)); 5600988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5601988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen writer.flush(); 5602988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5603988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen 5604988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen private String dump(DatabaseHelper dbh, boolean dumpDbLog) { 5605988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen StringBuilder s = new StringBuilder(); 5606988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mName); 5607988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(": "); 5608988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen SQLiteDatabase db = dbh.getReadableDatabase(); 5609988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (db == null) { 5610988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("null"); 5611988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } else { 5612988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("version " + db.getVersion() + ", "); 5613988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null); 5614988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen try { 5615988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (c != null && c.moveToFirst()) { 5616988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen int num = c.getInt(0); 5617988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(num + " rows, "); 5618988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } else { 5619988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("couldn't get row count, "); 5620988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5621988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } finally { 5622988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (c != null) { 5623988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen c.close(); 5624988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5625988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5626988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumInserts + " inserts, "); 5627988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumUpdates + " updates, "); 5628988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumDeletes + " deletes, "); 5629988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(dbh.mNumQueries + " queries, "); 5630988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (dbh.mScanStartTime != 0) { 5631988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("scan started " + DateUtils.formatDateTime(getContext(), 5632988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen dbh.mScanStartTime / 1000, 5633988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen DateUtils.FORMAT_SHOW_DATE 5634988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen | DateUtils.FORMAT_SHOW_TIME 5635988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen | DateUtils.FORMAT_ABBREV_ALL)); 5636988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen long now = dbh.mScanStopTime; 5637988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (now < dbh.mScanStartTime) { 5638988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen now = SystemClock.currentTimeMicro(); 5639988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5640988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(" (" + DateUtils.formatElapsedTime( 5641988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen (now - dbh.mScanStartTime) / 1000000) + ")"); 5642988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (dbh.mScanStopTime < dbh.mScanStartTime) { 5643988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (mMediaScannerVolume != null && 5644988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen dbh.mName.startsWith(mMediaScannerVolume)) { 5645988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(" (ongoing)"); 564610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } else { 5647988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append(" (scanning " + mMediaScannerVolume + ")"); 5648988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5649988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5650988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 5651988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (dumpDbLog) { 5652988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen c = db.query("log", new String[] {"time", "message"}, 5653f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen null, null, null, null, "rowid"); 5654988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen try { 5655988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen if (c != null) { 5656988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen while (c.moveToNext()) { 5657988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen String when = c.getString(0); 5658988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen String msg = c.getString(1); 5659988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen s.append("\n" + when + " : " + msg); 5660988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen } 566110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 566210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } finally { 566310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen if (c != null) { 566410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen c.close(); 566510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 566610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 566710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 566810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 5669988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen return s.toString(); 567010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen } 5671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project} 5672