MediaProvider.java revision efe0bcdf5ed215a5cb76c8a48aad1333056636f4
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 19702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager; 20bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.BroadcastReceiver; 21bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ComponentName; 22bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProvider; 23bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderOperation; 24bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderResult; 25bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentResolver; 26bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentUris; 27bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentValues; 28bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Context; 29bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Intent; 30bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.IntentFilter; 31bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.OperationApplicationException; 32bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ServiceConnection; 33ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.content.SharedPreferences; 34bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher; 3570676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources; 36702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor; 37ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils; 380027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor; 39702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.SQLException; 40702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase; 41702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper; 42702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder; 43702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap; 44702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory; 45b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile; 46702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner; 47b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile; 4890345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants; 49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri; 50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder; 51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment; 52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils; 53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler; 54ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread; 55702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message; 56702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor; 57702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process; 58d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException; 594f2186758ee1c6eaa702bf1511b233b26143b631Mike Lockwoodimport android.os.SystemProperties; 60ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager; 61702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns; 62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore; 63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio; 645bf5c26a3499d584332074baab97392696ed9614Ray Chenimport android.provider.MediaStore.Files; 6570676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns; 66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images; 6770676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns; 68702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns; 69702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video; 70702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils; 71702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log; 72702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File; 74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream; 75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException; 76702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException; 77702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream; 78702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.text.Collator; 79cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList; 80702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap; 81702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet; 82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator; 83f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List; 84b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue; 858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack; 86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 87702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/** 88702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details. 89702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the 90702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index). The content visible at content://media/external/... 91702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card. 92702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 93702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider { 94702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final Uri MEDIA_URI = Uri.parse("content://media"); 95702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart"); 96b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int ALBUM_THUMB = 1; 97b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int IMAGE_THUMB = 2; 98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 99702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>(); 100d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>(); 101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // A HashSet of paths that are pending creation of album art thumbnails. 1038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private HashSet mPendingThumbs = new HashSet(); 1048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 1058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // A Stack of outstanding thumbnail requests. 1068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private Stack mThumbRequestStack = new Stack(); 1078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 10820434e032e498b716f87cce2f23dd646819218bfRay Chen // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest. 10920434e032e498b716f87cce2f23dd646819218bfRay Chen private MediaThumbRequest mCurrentThumbRequest = null; 110b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private PriorityQueue<MediaThumbRequest> mMediaThumbQueue = 111b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL, 112b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaThumbRequest.getComparator()); 113b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 11417ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood private static String mExternalStoragePath; 115f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood private boolean mCaseInsensitivePaths; 11617ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood 117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // For compatibility with the approximately 0 apps that used mediaprovider search in 118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // releases 1.0, 1.1 or 1.5 119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsLegacy = new String[] { 120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 121a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 122a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist + 123a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album + 124a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE " + R.drawable.ic_search_category_music_song + " END END" + 125a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1, 126a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2, 127a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, 128a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY, 129a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE when grouporder=1 THEN data1 ELSE artist END AS data1", 130a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE when grouporder=1 THEN data2 ELSE " + 131a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2", 132a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "match as ar", 133a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen SearchManager.SUGGEST_COLUMN_INTENT_DATA, 134a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "grouporder", 135ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that 136ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen // column is not available here, and the list is already sorted. 137a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 138a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsFancy = new String[] { 139a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 140a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 141a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Artists.ARTIST, 142a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Albums.ALBUM, 143a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.TITLE, 144a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "data1", 145a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "data2", 146a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 14763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath // If this array gets changed, please update the constant below to point to the correct item. 148a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private String[] mSearchColsBasic = new String[] { 149a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen android.provider.BaseColumns._ID, 150a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen MediaStore.Audio.Media.MIME_TYPE, 151a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist + 152a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album + 153a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen " ELSE " + R.drawable.ic_search_category_music_song + " END END" + 154a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1, 155a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, 156a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY, 15763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath "(CASE WHEN grouporder=1 THEN '%1'" + // %1 gets replaced with localized string. 15863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" + 159e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" + 16063f748ff8b258d9110038778a006b3000164fbeeSatish Sampath " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2, 161a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen SearchManager.SUGGEST_COLUMN_INTENT_DATA 162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen }; 16363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath // Position of the TEXT_2 item in the above array. 16463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath private final int SEARCH_COLUMN_BASIC_TEXT2 = 5; 165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 1661717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood private static final String[] mMediaTableColumns = new String[] { 16716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood FileColumns._ID, 168afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood FileColumns.MEDIA_TYPE, 1691717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood }; 1701717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 171a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart"); 172a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen 173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() { 174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onReceive(Context context, Intent intent) { 176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) { 177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Remove the external volume and then notify all cursors backed by 178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // data on that volume 179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project detachVolume(Uri.parse("content://media/external")); 180d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen sFolderArtMap.clear(); 181b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MiniThumbFile.reset(); 182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 186d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // set to disable sending events when the operation originates from MTP 187d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private boolean mDisableMtpObjectCallbacks; 188d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 189d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private final SQLiteDatabase.CustomFunction mObjectRemovedCallback = 190d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood new SQLiteDatabase.CustomFunction() { 191d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void callback(String[] args) { 192d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // do nothing if the operation originated from MTP 193d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (mDisableMtpObjectCallbacks) return; 194d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 195d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood Log.d(TAG, "object removed " + args[0]); 196d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood IMtpService mtpService = mMtpService; 197d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (mtpService != null) { 198d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood try { 199d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood sendObjectRemoved(Integer.parseInt(args[0])); 200d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } catch (NumberFormatException e) { 201d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e); 202d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 203d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 204d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 205d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood }; 206d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Wrapper class for a specific database (associated with one particular 209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * external card, or with internal storage). Can open the actual database 210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * on demand, create and upgrade the schema, etc. 211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 212d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private final class DatabaseHelper extends SQLiteOpenHelper { 213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final Context mContext; 2145524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood final String mName; 215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final boolean mInternal; // True if this is the internal database 2165524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood boolean mUpgradeAttempted; // Used for upgrade error handling 217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // In memory caches of artist and album data. 219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> mArtistCache = new HashMap<String, Long>(); 220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> mAlbumCache = new HashMap<String, Long>(); 221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public DatabaseHelper(Context context, String name, boolean internal) { 223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project super(context, name, null, DATABASE_VERSION); 224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext = context; 2255524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mName = name; 226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mInternal = internal; 227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Creates database the first time we try to open it. 231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onCreate(final SQLiteDatabase db) { 234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project updateDatabase(db, mInternal, 0, DATABASE_VERSION); 235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Updates the database format when a new content provider is used 239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * with an older database format. 240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) { 2435524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mUpgradeAttempted = true; 244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project updateDatabase(db, mInternal, oldV, newV); 245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 247db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin @Override 248db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin public synchronized SQLiteDatabase getWritableDatabase() { 2495524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood SQLiteDatabase result = null; 2505524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mUpgradeAttempted = false; 2515524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood try { 2525524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood result = super.getWritableDatabase(); 2535524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } catch (Exception e) { 2545524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood if (!mUpgradeAttempted) { 2555524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood Log.e(TAG, "failed to open database " + mName, e); 2565524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood return null; 2575524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 2585524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 2595524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood 2605524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // If we failed to open the database during an upgrade, delete the file and try again. 2615524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // This will result in the creation of a fresh database, which will be repopulated 2625524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood // when the media scanner runs. 2635524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood if (result == null && mUpgradeAttempted) { 2645524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood mContext.getDatabasePath(mName).delete(); 2655524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood result = super.getWritableDatabase(); 2665524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 2675524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood return result; 2685524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood } 2695524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood 270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 271993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * For devices that have removable storage, we support keeping multiple databases 272993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * to allow users to switch between a number of cards. 273993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood * On such devices, touch this particular database and garbage collect old databases. 274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * An LRU cache system is used to clean up databases for old external 275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * storage volumes. 276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void onOpen(SQLiteDatabase db) { 27936d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky 280652c337bac290c7dfcde8725e7c771193a7f7ed6Vasu Nori // Turn on WAL optimization 281652c337bac290c7dfcde8725e7c771193a7f7ed6Vasu Nori db.enableWriteAheadLogging(); 28236d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky 283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mInternal) return; // The internal database is kept separately. 284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 285d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback); 286d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 287993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // the code below is only needed on devices with removable storage 288993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (!Environment.isExternalStorageRemovable()) return; 289993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood 290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // touch the database file to show it is most recently used 291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(db.getPath()); 292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long now = System.currentTimeMillis(); 293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file.setLastModified(now); 294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used databases if we are over the limit 296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] databases = mContext.databaseList(); 297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count = databases.length; 298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int limit = MAX_EXTERNAL_DATABASES; 299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete external databases that have not been used in the past two months 301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long twoMonthsAgo = now - OBSOLETE_DATABASE_DB; 302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < databases.length; i++) { 303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File other = mContext.getDatabasePath(databases[i]); 304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) { 305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[i] = null; 306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (file.equals(other)) { 308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // reduce limit to account for the existence of the database we 309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // are about to open, which we removed from the list. 310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project limit--; 311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long time = other.lastModified(); 314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (time < twoMonthsAgo) { 315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]); 316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext.deleteDatabase(databases[i]); 317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[i] = null; 318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used databases until 324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we are no longer over the limit 325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (count > limit) { 326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int lruIndex = -1; 327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long lruTime = 0; 328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < databases.length; i++) { 330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (databases[i] != null) { 331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long time = mContext.getDatabasePath(databases[i]).lastModified(); 332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lruTime == 0 || time < lruTime) { 333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project lruIndex = i; 334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project lruTime = time; 335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // delete least recently used database 340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lruIndex != -1) { 341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]); 342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mContext.deleteDatabase(databases[lruIndex]); 343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project databases[lruIndex] = null; 344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count--; 345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 35034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // synchronize on mMtpServiceConnection when accessing mMtpService 351d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private IMtpService mMtpService; 352d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 353d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private final ServiceConnection mMtpServiceConnection = new ServiceConnection() { 354d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void onServiceConnected(ComponentName className, android.os.IBinder service) { 35534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (this) { 35634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = IMtpService.Stub.asInterface(service); 35734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 358d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 359d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 360d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood public void onServiceDisconnected(ComponentName className) { 36134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (this) { 36234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 36334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 364d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 365d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood }; 366d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 367ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood private static final String[] sDefaultFolderNames = { 368ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_MUSIC, 369ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_PODCASTS, 370ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_RINGTONES, 371ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_ALARMS, 372ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_NOTIFICATIONS, 373ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_PICTURES, 374ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_MOVIES, 375ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_DOWNLOADS, 376ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood Environment.DIRECTORY_DCIM, 377ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood }; 378ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 379ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // creates default folders (Music, Downloads, etc) 380ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood private void createDefaultFolders(SQLiteDatabase db) { 381ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // Use a SharedPreference to ensure we only do this once. 382ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // We don't want to annoy the user by recreating the directories 383ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // after she has deleted them. 384ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); 385ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood if (prefs.getInt("created_default_folders", 0) == 0) { 386ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood for (String folderName : sDefaultFolderNames) { 387ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood File file = Environment.getExternalStoragePublicDirectory(folderName); 388ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood if (!file.exists()) { 389ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood file.mkdirs(); 390ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood insertDirectory(db, file.getAbsolutePath()); 391ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 392ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 393ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 394ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood SharedPreferences.Editor e = prefs.edit(); 395ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.clear(); 396ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.putInt("created_default_folders", 1); 397ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood e.commit(); 398ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 399ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 400ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public boolean onCreate() { 403d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood final Context context = getContext(); 404d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 405acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " + 406acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums._ID); 407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album"); 408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key"); 409acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " + 410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.FIRST_YEAR); 411acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " + 412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.LAST_YEAR); 413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist"); 414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist"); 415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key"); 416acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " + 417acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums.NUMBER_OF_SONGS); 418acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " + 419acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen MediaStore.Audio.Albums.ALBUM_ART); 420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 42163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] = 42263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll( 423d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood "%1", context.getString(R.string.artist_label)); 424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mDatabases = new HashMap<String, DatabaseHelper>(); 425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project attachVolume(INTERNAL_VOLUME); 426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT); 428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project iFilter.addDataScheme("file"); 429d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood context.registerReceiver(mUnmountReceiver, iFilter); 430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 43117ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath(); 432f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood mCaseInsensitivePaths = !context.getResources().getBoolean( 433f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood com.android.internal.R.bool.config_caseSensitiveExternalStorage); 434f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood if (!Process.supportsProcesses()) { 435f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood // Simulator uses host file system, so it should be case sensitive. 436f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood mCaseInsensitivePaths = false; 437f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood } 4384f2186758ee1c6eaa702bf1511b233b26143b631Mike Lockwood 439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // open external database if external storage is mounted 440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String state = Environment.getExternalStorageState(); 441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (Environment.MEDIA_MOUNTED.equals(state) || 442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { 443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project attachVolume(EXTERNAL_VOLUME); 444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 446ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND); 447ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang ht.start(); 448ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang mThumbHandler = new Handler(ht.getLooper()) { 449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public void handleMessage(Message msg) { 451b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (msg.what == IMAGE_THUMB) { 452b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mMediaThumbQueue) { 45320434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest = mMediaThumbQueue.poll(); 454b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 45520434e032e498b716f87cce2f23dd646819218bfRay Chen if (mCurrentThumbRequest == null) { 456b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Log.w(TAG, "Have message but no request?"); 457b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } else { 458b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen try { 45920434e032e498b716f87cce2f23dd646819218bfRay Chen File origFile = new File(mCurrentThumbRequest.mPath); 4604d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen if (origFile.exists() && origFile.length() > 0) { 46120434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.execute(); 4624d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } else { 4634d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen // original file hasn't been stored yet 4644d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen synchronized (mMediaThumbQueue) { 46520434e032e498b716f87cce2f23dd646819218bfRay Chen Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath); 4664d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } 4674d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen } 468b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } catch (IOException ex) { 4691d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen Log.w(TAG, ex); 4701d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen } catch (UnsupportedOperationException ex) { 4711d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen // This could happen if we unplug the sd card during insert/update/delete 4721d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen // See getDatabaseForUri. 4731d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen Log.w(TAG, ex); 47422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren } catch (OutOfMemoryError err) { 47522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren /* 47622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * Note: Catching Errors is in most cases considered 47722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * bad practice. However, in this case it is 47822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * motivated by the fact that corrupt or very large 47922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * images may cause a huge allocation to be 48022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * requested and denied. The bitmap handling API in 48122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * Android offers no other way to guard against 48222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren * these problems than by catching OutOfMemoryError. 48322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren */ 48422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren Log.w(TAG, err); 485b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } finally { 48620434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mCurrentThumbRequest) { 48720434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE; 48820434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.notifyAll(); 489b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 490b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 491b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 492b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } else if (msg.what == ALBUM_THUMB) { 493b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ThumbData d; 494b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mThumbRequestStack) { 495b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen d = (ThumbData)mThumbRequestStack.pop(); 496b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 4978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 498b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen makeThumbInternal(d); 499b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mPendingThumbs) { 500b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen mPendingThumbs.remove(d.path); 501b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 5028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return true; 507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 509afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String IMAGE_COLUMNS = 510afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_size,_display_name,mime_type,title,date_added," + 511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified,description,picasa_id,isprivate,latitude,longitude," + 512afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name"; 513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 514805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen private static final String AUDIO_COLUMNSv99 = 515afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_display_name,_size,mime_type,date_added," + 516afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified,title,title_key,duration,artist_id,composer,album_id," + 517afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 518afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bookmark"; 519afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 520805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen private static final String AUDIO_COLUMNSv100 = 521805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "_data,_display_name,_size,mime_type,date_added," + 522805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "date_modified,title,title_key,duration,artist_id,composer,album_id," + 523805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," + 524805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen "bookmark,album_artist"; 525805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen 526afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String VIDEO_COLUMNS = 527afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data,_display_name,_size,mime_type,date_added,date_modified," + 528afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title,duration,artist,album,resolution,description,isprivate,tags," + 529afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "category,language,mini_thumb_data,latitude,longitude,datetaken," + 530afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_magic,bucket_id,bucket_display_name, bookmark"; 531afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 532afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified"; 533afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * This method takes care of updating all the tables in the database to the 536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * current version, creating them if necessary. 537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * This method can only update databases at schema 63 or higher, which was 538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * created August 1, 2008. Older database will be cleared and recreated. 539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db Database 540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param internal True if this is the internal media database 541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void updateDatabase(SQLiteDatabase db, boolean internal, 543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int fromVersion, int toVersion) { 544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // sanity checks 546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (toVersion != DATABASE_VERSION) { 547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + 548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DATABASE_VERSION); 549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException(); 550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (fromVersion > toVersion) { 55195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion + 552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " to " + toVersion + ". Did you forget to wipe data?"); 553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException(); 554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 556d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag. 557acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // We can't downgrade from those revisions, so start over. 558022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen // (the initial change to do this was wrong, so now we actually need to start over 559022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen // if the database version is 84-89) 560bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair. 561bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // However version 91 was reused in a divergent development path for gingerbread, 562bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // so we need to support upgrades from 91. 563bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Therefore we will only force a reset for versions 92 - 94. 564d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) || 565bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin (fromVersion >= 92 && fromVersion <= 94)) { 566acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen fromVersion = 63; 567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Drop everything and start over. 568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.i(TAG, "Upgrading media database from version " + 569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fromVersion + " to " + toVersion + ", which will destroy all old data"); 570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS images"); 571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS images_cleanup"); 572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS thumbnails"); 573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup"); 574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_meta"); 575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS artists"); 576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS albums"); 577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS album_art"); 578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS artist_info"); 579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS album_info"); 580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS artists_albums_map"); 581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_genres"); 583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_genres_map"); 584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup"); 585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_playlists"); 586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS audio_playlists_map"); 587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup"); 588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1"); 589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2"); 590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TABLE IF EXISTS video"); 591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS video_cleanup"); 592cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood db.execSQL("DROP TABLE IF EXISTS objects"); 5939ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup"); 5949ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup"); 5959ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup"); 5969ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup"); 597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS images (" + 599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + 601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT," + 605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "description TEXT," + 608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "picasa_id TEXT," + 609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "isprivate INTEGER," + 610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "latitude DOUBLE," + 611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "longitude DOUBLE," + 612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "datetaken INTEGER," + 613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "orientation INTEGER," + 614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_magic INTEGER," + 615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "bucket_id TEXT," + 616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "bucket_display_name TEXT" + 617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);"); 620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " + 622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM thumbnails WHERE image_id = old._id;" + 624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 627b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // create image thumbnail table 628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" + 629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + 631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "image_id INTEGER," + 632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "kind INTEGER," + 633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "width INTEGER," + 634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "height INTEGER" + 635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);"); 638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " + 640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains meta data about audio files 645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" + 646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 647216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen "_data TEXT UNIQUE NOT NULL," + 648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT NOT NULL," + 654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title_key TEXT NOT NULL," + 655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "duration INTEGER," + 656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_id INTEGER," + 657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "composer TEXT," + 658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER," + 659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "track INTEGER," + // track is an integer to allow proper sorting 660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "year INTEGER CHECK(year!=0)," + 661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_ringtone INTEGER," + 662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_music INTEGER," + 663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_alarm INTEGER," + 664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "is_notification INTEGER" + 665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains a sort/group "key" and the preferred display name for artists 668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS artists (" + 669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_id INTEGER PRIMARY KEY," + 670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist_key TEXT NOT NULL UNIQUE," + 671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist TEXT NOT NULL" + 672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains a sort/group "key" and the preferred display name for albums 675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS albums (" + 676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER PRIMARY KEY," + 677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_key TEXT NOT NULL UNIQUE," + 678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album TEXT NOT NULL" + 679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" + 682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album_id INTEGER PRIMARY KEY," + 683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT" + 684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project recreateAudioView(db); 68795ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides some extra info about artists, like the number of tracks 690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // and albums for this artist 691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " + 692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT artist_id AS _id, artist, artist_key, " + 693acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "COUNT(DISTINCT album) AS number_of_albums, " + 694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+ 695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "GROUP BY artist_key;"); 696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides extra info albums, such as the number of tracks 698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " + 699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT audio.album_id AS _id, album, album_key, " + 700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "MIN(year) AS minyear, " + 701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "MAX(year) AS maxyear, artist, artist_id, artist_key, " + 702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS + 703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ",album_art._data AS album_art" + 704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" + 705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " WHERE is_music=1 GROUP BY audio.album_id;"); 706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 707acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // For a given artist_id, provides the album_id for albums on 708acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // which the artist appears. 709acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " + 710acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "SELECT DISTINCT artist_id, album_id FROM audio_meta;"); 711acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /* 713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Only external media volumes can handle genres, playlists, etc. 714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!internal) { 716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio file is deleted 717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " + 718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_genres_map WHERE audio_id = old._id;" + 720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" + 721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains audio genre definitions 724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" + 725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "name TEXT NOT NULL" + 727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contiains mappings between audio genres and audio files 730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" + 731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_id INTEGER NOT NULL," + 733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "genre_id INTEGER NOT NULL" + 734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio genre is delete 737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " + 738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_genres_map WHERE genre_id = old._id;" + 740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains audio playlist definitions 743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" + 744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT," + // _data is path for file based playlists, or null 746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "name TEXT NOT NULL," + 747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER" + 749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains mappings between audio playlists and audio files 752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" + 753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_id INTEGER NOT NULL," + 755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "playlist_id INTEGER NOT NULL," + 756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "play_order INTEGER NOT NULL" + 757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up when an audio playlist is deleted 760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " + 761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up album_art table entry when an album is deleted 767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " + 768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE FROM album_art WHERE album_id = old.album_id;" + 770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Cleans up album_art when an album is deleted 773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " + 774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Contains meta data about video files 780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TABLE IF NOT EXISTS video (" + 781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_id INTEGER PRIMARY KEY," + 782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_data TEXT NOT NULL," + 783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_display_name TEXT," + 784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "_size INTEGER," + 785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mime_type TEXT," + 786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_added INTEGER," + 787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "date_modified INTEGER," + 788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "title TEXT," + 789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "duration INTEGER," + 790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "artist TEXT," + 791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "album TEXT," + 792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "resolution TEXT," + 793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "description TEXT," + 794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "isprivate INTEGER," + // for YouTube videos 795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "tags TEXT," + // for YouTube videos 796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "category TEXT," + // for YouTube videos 797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "language TEXT," + // for YouTube videos 798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_data TEXT," + 799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "latitude DOUBLE," + 800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "longitude DOUBLE," + 801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "datetaken INTEGER," + 802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "mini_thumb_magic INTEGER" + 803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " + 806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _DELETE_FILE(old._data);" + 808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // At this point the database is at least at schema version 63 (it was 812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // either created at version 63 by the code above, or was already at 813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // version 63 or later) 814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 64) { 816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the index that updates the database to schema version 64 817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);"); 818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 820acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 821acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.0 shipped with database version 64 822acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 823acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 65) { 825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the index that updates the database to schema version 65 826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);"); 827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 829403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // In version 66, originally we updateBucketNames(db, "images"), 830403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // but we need to do it in version 89 and therefore save the update here. 831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 67) { 833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create the indices that update the database to schema version 67 834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);"); 835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);"); 836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 68) { 839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create bucket_id and bucket_display_name columns for the video table. 840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;"); 841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT"); 842403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen 843403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // In version 68, originally we updateBucketNames(db, "video"), 844403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen // but we need to do it in version 89 and therefore save the update here. 845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 69) { 848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project updateDisplayName(db, "images"); 849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 70) { 852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create bookmark column for the video table. 853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;"); 854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 85595ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 71) { 857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // There is no change to the database schema, however a code change 858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // fixed parsing of metadata for certain files bought from the 859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // iTunes music store, so we want to rescan files that might need it. 860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // We do this by clearing the modification date in the database for 861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // those files, so that the media scanner will see them as updated 862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // and rescan them. 863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" + 864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "SELECT _id FROM audio where mime_type='audio/mp4' AND " + 865e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "artist='" + MediaStore.UNKNOWN_STRING + "' AND " + 866e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "album='" + MediaStore.UNKNOWN_STRING + "'" + 867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ");"); 868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 86995ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (fromVersion < 72) { 871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Create is_podcast and bookmark columns for the audio table. 872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;"); 873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';"); 874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" + 875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND _data NOT LIKE '%/music/%';"); 876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;"); 877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // New columns added to tables aren't visible in views on those tables 879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // without opening and closing the database (or using the 'vacuum' command, 880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // which we can't do here because all this code runs inside a transaction). 881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // To work around this, we drop and recreate the affected view and trigger. 882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project recreateAudioView(db); 883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 88495ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 885acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 886acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.5 shipped with database version 72 887acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 888acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 8898d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen if (fromVersion < 73) { 8908d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // There is no change to the database schema, but we now do case insensitive 8918d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // matching of folder names when determining whether something is music, a 8928d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen // ringtone, podcast, etc, so we might need to reclassify some files. 8938d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " + 8948d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/music/%';"); 8958d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " + 8968d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/ringtones/%';"); 8978d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " + 8988d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/notifications/%';"); 8998d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " + 9008d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/alarms/%';"); 9018d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " + 9028d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen "_data LIKE '%/podcasts/%';"); 9038d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen } 904a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 905a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (fromVersion < 74) { 906a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // This view is used instead of the audio view by the union below, to force 907a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // sqlite to use the title_key index. This greatly reduces memory usage 908a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // (no separate copy pass needed for sorting, which could cause errors on 909a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // large datasets) and improves speed (by about 35% on a large dataset) 910a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " + 911a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "ORDER BY title_key;"); 912a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 913a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS search AS " + 914a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT _id," + 915a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'artist' AS mime_type," + 916a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 917a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS album," + 918a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS title," + 919a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text1," + 920a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS text2," + 921a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "number_of_albums AS data1," + 922a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "number_of_tracks AS data2," + 923a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key AS match," + 924a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/artists/'||_id AS suggest_intent_data," + 925a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "1 AS grouporder " + 926e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " + 927a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "UNION ALL " + 928a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT _id," + 929a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'album' AS mime_type," + 930a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 931a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album," + 932a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS title," + 933a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album AS text1," + 934a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text2," + 935a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data1," + 936a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data2," + 937a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key||' '||album_key AS match," + 938a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/albums/'||_id AS suggest_intent_data," + 939a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "2 AS grouporder " + 940e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " + 941a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "UNION ALL " + 942a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "SELECT searchhelpertitle._id AS _id," + 943a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "mime_type," + 944a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist," + 945a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "album," + 946a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "title," + 947a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "title AS text1," + 948a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist AS text2," + 949a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data1," + 950a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "NULL AS data2," + 951a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "artist_key||' '||album_key||' '||title_key AS match," + 952a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "'content://media/external/audio/media/'||searchhelpertitle._id AS " + 953a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "suggest_intent_data," + 954a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "3 AS grouporder " + 955a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "FROM searchhelpertitle WHERE (title != '') " 956a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen ); 957a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } 95859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 95959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (fromVersion < 75) { 96095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen // Force a rescan of the audio entries so we can apply the new logic to 96159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // distinguish same-named albums. 96259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen db.execSQL("UPDATE audio_meta SET date_modified=0;"); 96359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen db.execSQL("DELETE FROM albums"); 96459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 96515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen 96615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen if (fromVersion < 76) { 96715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen // We now ignore double quotes when building the key, so we have to remove all of them 96815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen // from existing keys. 96915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE audio_meta SET title_key=" + 97015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(title_key,x'081D08C29F081D',x'081D') " + 97115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';"); 97215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE albums SET album_key=" + 97315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(album_key,x'081D08C29F081D',x'081D') " + 97415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';"); 97515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen db.execSQL("UPDATE artists SET artist_key=" + 97615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "REPLACE(artist_key,x'081D08C29F081D',x'081D') " + 97715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';"); 97815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen } 979b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 980acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 981acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 1.6 shipped with database version 76 982acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 983acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 984b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (fromVersion < 77) { 985b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // create video thumbnail table 986b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" + 987b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "_id INTEGER PRIMARY KEY," + 988b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "_data TEXT," + 989b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "video_id INTEGER," + 990b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "kind INTEGER," + 991b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "width INTEGER," + 992b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "height INTEGER" + 993b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ");"); 994b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 995b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);"); 996b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 997b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " + 998b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "BEGIN " + 999b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "SELECT _DELETE_FILE(old._data);" + 1000b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "END"); 1001b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 10021769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen 1003acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1004acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 2.0 and 2.0.1 shipped with database version 77 1005acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1006acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 10071769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen if (fromVersion < 78) { 1008044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen // Force a rescan of the video entries so we can update 10091769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen // latest changed DATE_TAKEN units (in milliseconds). 10101769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen db.execSQL("UPDATE video SET date_modified=0;"); 10111769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen } 1012268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen 1013acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* 1014acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen * Android 2.1 shipped with database version 78 1015acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen */ 1016acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen 1017268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if (fromVersion < 79) { 1018268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // move /sdcard/albumthumbs to 1019268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // /sdcard/Android/data/com.android.providers.media/albumthumbs, 1020268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // and update the database accordingly 1021268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen 102217ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood String oldthumbspath = mExternalStoragePath + "/albumthumbs"; 102317ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood String newthumbspath = mExternalStoragePath + "/" + ALBUM_THUMB_FOLDER; 1024268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen File thumbsfolder = new File(oldthumbspath); 1025268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if (thumbsfolder.exists()) { 1026268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // move folder to its new location 1027268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen File newthumbsfolder = new File(newthumbspath); 1028268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen newthumbsfolder.getParentFile().mkdirs(); 1029268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen if(thumbsfolder.renameTo(newthumbsfolder)) { 1030268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen // update the database 1031268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" + 1032268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen oldthumbspath + "','" + newthumbspath + "');"); 1033268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1034268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1035268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen } 1036044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen 1037044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen if (fromVersion < 80) { 1038044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen // Force rescan of image entries to update DATE_TAKEN as UTC timestamp. 1039044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen db.execSQL("UPDATE images SET date_modified=0;"); 1040044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen } 10410ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 1042166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen if (fromVersion < 81 && !internal) { 10430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Delete entries starting with /mnt/sdcard. This is for the benefit 10440ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // of users running builds between 2.0.1 and 2.1 final only, since 10450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // users updating from 2.0 or earlier will not have such entries. 10460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 10470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // First we need to update the _data fields in the affected tables, since 10480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // otherwise deleting the entries will also delete the underlying files 10490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // (via a trigger), and we want to keep them. 10500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 10510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 10520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 10530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 10540ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 10550ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 1056216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';"); 10570ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Once the paths have been renamed, we can safely delete the entries 10580ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';"); 10590ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM images WHERE _data IS '////';"); 10600ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM video WHERE _data IS '////';"); 10610ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';"); 10620ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';"); 10630ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM audio_meta WHERE _data IS '////';"); 10640ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE FROM album_art WHERE _data IS '////';"); 10650ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 10660ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // rename existing entries starting with /sdcard to /mnt/sdcard 10670ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_meta" + 10680ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 10690ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_playlists" + 10700ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 10710ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE images" + 10720ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 10730ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE video" + 10740ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 10750ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE videothumbnails" + 10760ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 10770ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE thumbnails" + 10780ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 10790ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE album_art" + 10800ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';"); 10810ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen 10820ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // Delete albums and artists, then clear the modification time on songs, which 10830ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // will cause the media scanner to rescan everything, rebuilding the artist and 10840ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // album tables along the way, while preserving playlists. 10850ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // We need this rescan because ICU also changed, and now generates different 10860ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen // collation keys 10870ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE from albums"); 10880ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("DELETE from artists"); 10890ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen db.execSQL("UPDATE audio_meta SET date_modified=0;"); 10900ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen } 109184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen 109284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen if (fromVersion < 82) { 1093acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // recreate this view with the correct "group by" specifier 109484403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen db.execSQL("DROP VIEW IF EXISTS artist_info"); 109584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " + 109684403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "SELECT artist_id AS _id, artist, artist_key, " + 1097acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "COUNT(DISTINCT album_key) AS number_of_albums, " + 109884403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+ 109984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen "GROUP BY artist_key;"); 110084403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen } 1101216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen 1102acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen /* we skipped over version 83, and reverted versions 84, 85 and 86 */ 1103ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen 1104ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen if (fromVersion < 87) { 1105ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // The fastscroll thumb needs an index on the strings being displayed, 1106ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // otherwise the queries it does to determine the correct position 1107ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen // becomes really inefficient 1108022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);"); 1109022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);"); 1110022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);"); 1111ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen } 1112216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen 1113acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen if (fromVersion < 88) { 1114acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // Clean up a few more things from versions 84/85/86, and recreate 1115acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // the few things worth keeping from those changes. 1116acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update1;"); 1117acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update2;"); 1118acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update3;"); 1119acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS albums_update4;"); 1120acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update1;"); 1121acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update2;"); 1122acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update3;"); 1123acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("DROP TRIGGER IF EXISTS artist_update4;"); 112416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP VIEW IF EXISTS album_artists;"); 1125acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);"); 1126acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);"); 1127acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // For a given artist_id, provides the album_id for albums on 1128acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen // which the artist appears. 1129acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " + 1130acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "SELECT DISTINCT artist_id, album_id FROM audio_meta;"); 1131acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen } 1132403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen 1133fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // In version 89, originally we updateBucketNames(db, "images") and 1134fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames 1135fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // for all files and therefore can save the update here. 1136b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 1137b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (fromVersion < 91) { 1138bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // Never query by mini_thumb_magic_index 1139bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index"); 1140bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin 1141bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin // sort the items by taken date in each bucket 1142bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)"); 1143bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)"); 1144bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin } 1145bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin 1146afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // versions 92 - 98 were work in progress on MTP obsoleted by version 99 114786f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen if (fromVersion < 92) { 114886f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen // Delete albums and artists, then clear the modification time on songs, which 114986f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen // will cause the media scanner to rescan everything, rebuilding the artist and 115086f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen // album tables along the way, while preserving playlists. 115186f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen // We need this rescan because ICU also changed, and now generates different 115286f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen // collation keys 115386f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen db.execSQL("DELETE from albums"); 115486f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen db.execSQL("DELETE from artists"); 115586f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen db.execSQL("UPDATE audio_meta SET date_modified=0;"); 115686f165b636baa8c6e0f4568fb07699ff5cfbcf8aMarco Nelissen } 1157a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu 1158afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (fromVersion < 99) { 1159afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Remove various stages of work in progress for MTP support 1160afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS objects"); 1161afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS files"); 116216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;"); 116316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;"); 116416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;"); 116516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;"); 1166afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;"); 1167afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;"); 1168afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;"); 1169afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;"); 1170afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;"); 1171afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1172afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Create a new table to manage all files in our storage. 1173afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // This contains a union of all the columns from the old 1174afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // images, audio_meta, videos and audio_playlist tables. 1175afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TABLE files (" + 117600a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen "_id INTEGER PRIMARY KEY AUTOINCREMENT," + 1177afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_data TEXT," + // this can be null for playlists 1178afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_size INTEGER," + 1179afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "format INTEGER," + 1180afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "parent INTEGER," + 1181afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_added INTEGER," + 1182afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "date_modified INTEGER," + 1183afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mime_type TEXT," + 1184afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title TEXT," + 1185afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "description TEXT," + 1186afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "_display_name TEXT," + 1187afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1188afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for images 1189afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "picasa_id TEXT," + 1190afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "orientation INTEGER," + 1191afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1192afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for images and video 1193afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "latitude DOUBLE," + 1194afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "longitude DOUBLE," + 1195afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "datetaken INTEGER," + 1196afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_magic INTEGER," + 1197afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bucket_id TEXT," + 1198afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bucket_display_name TEXT," + 1199afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "isprivate INTEGER," + 1200afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1201afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for audio 1202afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "title_key TEXT," + 1203afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "artist_id INTEGER," + 1204afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "album_id INTEGER," + 1205afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "composer TEXT," + 1206afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "track INTEGER," + 1207afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "year INTEGER CHECK(year!=0)," + 1208afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_ringtone INTEGER," + 1209afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_music INTEGER," + 1210afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_alarm INTEGER," + 1211afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_notification INTEGER," + 1212afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "is_podcast INTEGER," + 1213afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1214afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for audio and video 1215afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "duration INTEGER," + 1216afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "bookmark INTEGER," + 1217afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1218afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for video 1219afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "artist TEXT," + 1220afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "album TEXT," + 1221afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "resolution TEXT," + 1222afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "tags TEXT," + 1223afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "category TEXT," + 1224afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "language TEXT," + 1225afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "mini_thumb_data TEXT," + 1226afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1227afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // for playlists 1228afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "name TEXT," + 1229afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1230afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // media_type is used by the views to emulate the old 1231afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // images, audio_meta, videos and audio_playlist tables. 1232afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "media_type INTEGER," + 1233afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1234afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Value of _id from the old media table. 1235afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Used only for updating other tables during database upgrade. 1236afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "old_id INTEGER" + 1237afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood ");"); 1238afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX path_index ON files(_data);"); 1239afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX media_type_index ON files(media_type);"); 1240afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1241afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Copy all data from our obsolete tables to the new files table 1242afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("INSERT INTO files (" + IMAGE_COLUMNS + ",old_id,media_type) SELECT " 1243afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + IMAGE_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;"); 1244805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen db.execSQL("INSERT INTO files (" + AUDIO_COLUMNSv99 + ",old_id,media_type) SELECT " 1245805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO + " FROM audio_meta;"); 1246afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("INSERT INTO files (" + VIDEO_COLUMNS + ",old_id,media_type) SELECT " 1247afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + VIDEO_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;"); 124816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood if (!internal) { 1249afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT " 1250afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST 1251afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " FROM audio_playlists;"); 1252afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 125316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood 1254afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Delete the old tables 1255afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS images"); 1256afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS audio_meta"); 1257afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS video"); 1258afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TABLE IF EXISTS audio_playlists"); 125916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood 1260afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Create views to replace our old tables 1261afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS + 1262afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1263afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_IMAGE + ";"); 1264805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen// audio_meta will be created below for schema 100 1265805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen// db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv99 + 1266805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen// " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1267805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen// + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1268afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS + 1269afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1270afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_VIDEO + ";"); 1271afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1272afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS + 1273afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1274afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_PLAYLIST + ";"); 127516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood } 127636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1277afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update the image_id column in the thumbnails table. 1278afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files " 1279afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = thumbnails.image_id AND files.media_type = " 1280afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_IMAGE + ");"); 128136339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1282afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1283afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update audio_id in the audio_genres_map and audio_playlists_map tables. 1284afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files " 1285afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = " 1286afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_AUDIO + ");"); 1287afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files " 1288afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = audio_playlists_map.audio_id " 1289afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");"); 1290afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 1291afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1292afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update video_id in the videothumbnails table. 1293afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files " 1294afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = " 1295afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + FileColumns.MEDIA_TYPE_VIDEO + ");"); 1296afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1297afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // update indices to work on the files table 1298afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS title_idx"); 1299afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS album_id_idx"); 1300afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS image_bucket_index"); 1301afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS video_bucket_index"); 1302afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS sort_index"); 1303afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS titlekey_index"); 1304afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP INDEX IF EXISTS artist_id_idx"); 1305afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX title_idx ON files(title);"); 1306afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX album_id_idx ON files(album_id);"); 1307afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);"); 1308afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);"); 1309afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX titlekey_index ON files(title_key);"); 1310afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);"); 1311afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1312afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Recreate triggers for our obsolete tables on the new files table 1313afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS images_cleanup"); 1314afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup"); 1315afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS video_cleanup"); 1316afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup"); 1317afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS audio_delete"); 131836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1319afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " + 1320afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " + 132136339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM thumbnails WHERE image_id = old._id;" + 1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 132436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 132536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1326afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " + 132749dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " + 132836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 133036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 133136339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (!internal) { 1333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " + 1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " + 1335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "BEGIN " + 1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_genres_map WHERE audio_id = old._id;" + 1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" + 1338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "END"); 1339afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " + 1341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " + 1342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "BEGIN " + 1343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" + 1344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT _DELETE_FILE(old._data);" + 1345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "END"); 1346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 1347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " + 134836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "BEGIN " + 1349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from files where _id=old._id;" + 1350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from audio_playlists_map where audio_id=old._id;" + 1351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "DELETE from audio_genres_map where audio_id=old._id;" + 135236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood "END"); 135336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood } 135436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood } 135536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood 1356805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen if (fromVersion < 100) { 1357805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen db.execSQL("ALTER TABLE files ADD COLUMN album_artist TEXT;"); 1358805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen db.execSQL("DROP VIEW IF EXISTS audio_meta;"); 1359805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 + 1360805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen " FROM files WHERE " + FileColumns.MEDIA_TYPE + "=" 1361805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1362805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 1363805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1364805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen } 1365805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen 1366fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood if (fromVersion < 300) { 1367fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // we now compute bucket and display names for all files to avoid problems with files 1368fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // that the media scanner might not recognize as images or videos 1369fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood updateBucketNames(db, "files"); 1370fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood } 1371fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood 1372db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin if (fromVersion < 301) { 1373db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("DROP INDEX IF EXISTS bucket_index"); 1374db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)"); 1375db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)"); 1376db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin } 1377db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin 137820405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood if (fromVersion < 302) { 137920405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood db.execSQL("CREATE INDEX parent_index ON files(parent);"); 138020405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood db.execSQL("CREATE INDEX format_index ON files(format);"); 138120405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood } 138220405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood 13832658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (fromVersion < 303) { 13842658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // the album disambiguator hash changed, so rescan songs and force 13852658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // albums to be updated. Artists are unaffected. 13862658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen db.execSQL("DELETE from albums"); 13872658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 13882658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 13892658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } 13902658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 13914b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood if (fromVersion < 304 && !internal) { 139251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood // notifies host when files are deleted 139351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " + 139451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "BEGIN " + 139551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "SELECT _OBJECT_REMOVED(old._id);" + 139651d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood "END"); 139751d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood 139851d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood } 139951d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood 14004b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood if (fromVersion < 305 && internal) { 14014b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood // version 304 erroneously added this trigger to the internal database 14024b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood db.execSQL("DROP TRIGGER IF EXISTS files_cleanup"); 14034b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood } 14044b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood 1405efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen if (fromVersion < 306) { 1406efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen // The genre list was expanded and genre string parsing was tweaked, so 1407efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen // rebuild the genre list 1408efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "=" 1409efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen + FileColumns.MEDIA_TYPE_AUDIO + ";"); 1410efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("DELETE FROM audio_genres_map"); 1411efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen db.execSQL("DELETE FROM audio_genres"); 1412efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen } 1413efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen 1414acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen sanityCheck(db, fromVersion); 14151d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen } 14161d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen 14171d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen /** 1418216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen * Perform a simple sanity check on the database. Currently this tests 1419216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen * whether all the _data entries in audio_meta are unique 1420216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen */ 1421216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen private static void sanityCheck(SQLiteDatabase db, int fromVersion) { 1422216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Cursor c1 = db.query("audio_meta", new String[] {"count(*)"}, 1423216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen null, null, null, null, null); 1424216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"}, 1425216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen null, null, null, null, null); 1426216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c1.moveToFirst(); 1427216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c2.moveToFirst(); 1428216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen int num1 = c1.getInt(0); 1429216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen int num2 = c2.getInt(0); 1430216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c1.close(); 1431216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c2.close(); 1432216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen if (num1 != num2) { 1433216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen Log.e(TAG, "audio_meta._data column is not unique while upgrading" + 1434216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen " from schema " +fromVersion + " : " + num1 +"/" + num2); 1435216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen // Delete all audio_meta rows so they will be rebuilt by the media scanner 1436216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen db.execSQL("DELETE FROM audio_meta;"); 1437216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } 1438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void recreateAudioView(SQLiteDatabase db) { 1441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Provides a unified audio/artist/album info view. 1442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Note that views are read-only, so we define a trigger to allow deletes. 1443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP VIEW IF EXISTS audio"); 1444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("DROP TRIGGER IF EXISTS audio_delete"); 1445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " + 1446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " + 1447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;"); 1448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " + 1450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "BEGIN " + 1451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE from audio_meta where _id=old._id;" + 1452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE from audio_playlists_map where audio_id=old._id;" + 1453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "DELETE from audio_genres_map where audio_id=old._id;" + 1454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "END"); 1455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 145695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen 1457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Iterate through the rows of a table in a database, ensuring that the bucket_id and 1459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * bucket_display_name columns are correct. 1460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db 1461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param tableName 1462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void updateBucketNames(SQLiteDatabase db, String tableName) { 1464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Rebuild the bucket_display_name column using the natural case rather than lower case. 1465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 1466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] columns = {BaseColumns._ID, MediaColumns.DATA}; 1468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor cursor = db.query(tableName, columns, null, null, null, null, null); 1469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID); 1471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA); 1472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor.moveToNext()) { 1473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = cursor.getString(dataColumnIndex); 1474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(); 1475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project computeBucketValues(data, values); 1476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int rowId = cursor.getInt(idColumnIndex); 1477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.update(tableName, values, "_id=" + rowId, null); 1478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project cursor.close(); 1481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 1483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 1485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Iterate through the rows of a table in a database, ensuring that the 1490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * display name column has a value. 1491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db 1492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param tableName 1493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void updateDisplayName(SQLiteDatabase db, String tableName) { 1495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Fill in default values for null displayName values 1496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 1497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME}; 1499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor cursor = db.query(tableName, columns, null, null, null, null, null); 1500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 1501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID); 1502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA); 1503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME); 1504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(); 1505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor.moveToNext()) { 1506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String displayName = cursor.getString(displayNameIndex); 1507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (displayName == null) { 1508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = cursor.getString(dataColumnIndex); 1509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.clear(); 1510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project computeDisplayName(data, values); 1511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int rowId = cursor.getInt(idColumnIndex); 1512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.update(tableName, values, "_id=" + rowId, null); 1513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project cursor.close(); 1517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 1519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 1520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 1521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param data The input path 1525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param values the content values, where the bucked id name and bucket display name are updated. 1526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 1527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void computeBucketValues(String data, ContentValues values) { 1530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File parentFile = new File(data).getParentFile(); 1531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (parentFile == null) { 1532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project parentFile = new File("/"); 1533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Lowercase the path for hashing. This avoids duplicate buckets if the 1536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // filepath case is changed externally. 1537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Keep the original case for display. 1538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String path = parentFile.toString().toLowerCase(); 1539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name = parentFile.getName(); 1540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the 1542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // same for both images and video. However, for backwards-compatibility reasons 1543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // there is no common base class. We use the ImageColumns version here 1544d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood values.put(ImageColumns.BUCKET_ID, path.hashCode()); 1545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(ImageColumns.BUCKET_DISPLAY_NAME, name); 1546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 1549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param data The input path 1550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param values the content values, where the display name is updated. 1551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 1552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 1553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static void computeDisplayName(String data, ContentValues values) { 1554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = (data == null ? "" : data.toString()); 1555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int idx = s.lastIndexOf('/'); 1556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (idx >= 0) { 1557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project s = s.substring(idx + 1); 1558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("_display_name", s); 1560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1562b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen /** 1563498b62c2912302a23532c73a028a7684c5df33caRay Chen * Copy taken time from date_modified if we lost the original value (e.g. after factory reset) 1564498b62c2912302a23532c73a028a7684c5df33caRay Chen * This works for both video and image tables. 1565b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen * 1566b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen * @param values the content values, where taken time is updated. 1567b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen */ 1568b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen private static void computeTakenTime(ContentValues values) { 1569b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen if (! values.containsKey(Images.Media.DATE_TAKEN)) { 1570b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen // This only happens when MediaScanner finds an image file that doesn't have any useful 1571b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen // reference to get this value. (e.g. GPSTimeStamp) 1572498b62c2912302a23532c73a028a7684c5df33caRay Chen Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED); 1573b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen if (lastModified != null) { 1574b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen values.put(Images.Media.DATE_TAKEN, lastModified * 1000); 1575b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 1576b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 1577b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen } 1578b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen 1579b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen /** 1580b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * This method blocks until thumbnail is ready. 1581b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * 1582b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * @param thumbUri 1583b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen * @return 1584b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen */ 1585b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private boolean waitForThumbnailReady(Uri origUri) { 1586b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA, 1587b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ImageColumns.MINI_THUMB_MAGIC}, null, null, null); 1588b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c == null) return false; 1589b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1590b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean result = false; 1591b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1592b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c.moveToFirst()) { 1593e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen long id = c.getLong(0); 1594b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String path = c.getString(1); 1595b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen long magic = c.getLong(2); 1596b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 15979299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen MediaThumbRequest req = requestMediaThumbnail(path, origUri, 15989299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen MediaThumbRequest.PRIORITY_HIGH, magic); 15999299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen if (req == null) { 16009299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen return false; 16019299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } 16029299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen synchronized (req) { 16039299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen try { 16049299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen while (req.mState == MediaThumbRequest.State.WAIT) { 16059299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen req.wait(); 160620434e032e498b716f87cce2f23dd646819218bfRay Chen } 16079299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } catch (InterruptedException e) { 16089299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen Log.w(TAG, e); 16099299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen } 16109299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen if (req.mState == MediaThumbRequest.State.DONE) { 16119299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen result = true; 1612b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1613b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1614b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1615b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen c.close(); 1616b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1617b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return result; 1618b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1619b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1620e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid, 1621e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean isVideo) { 1622e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean cancelAllOrigId = (id == -1); 1623e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean cancelAllGroupId = (gid == -1); 1624e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen return (req.mCallingPid == pid) && 1625e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (cancelAllGroupId || req.mGroupId == gid) && 1626e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (cancelAllOrigId || req.mOrigId == id) && 1627e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen (req.mIsVideo == isVideo); 1628e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } 1629e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 1630b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table, 1631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String column, boolean hasThumbnailId) { 1632b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.setTables(table); 1633b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (hasThumbnailId) { 1634b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // For uri dispatched to this method, the 4th path segment is always 1635b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // the thumbnail id. 1636b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.appendWhere("_id = " + uri.getPathSegments().get(3)); 1637b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // client already knows which thumbnail it wants, bypass it. 1638b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 1639b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1640b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen String origId = uri.getQueryParameter("orig_id"); 1641b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // We can't query ready_flag unless we know original id 1642b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (origId == null) { 1643b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // this could be thumbnail query for other purpose, bypass it. 1644b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 1645b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1646b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1647b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean needBlocking = "1".equals(uri.getQueryParameter("blocking")); 164820434e032e498b716f87cce2f23dd646819218bfRay Chen boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel")); 1649e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Uri origUri = uri.buildUpon().encodedPath( 1650e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen uri.getPath().replaceFirst("thumbnails", "media")) 1651e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen .appendPath(origId).build(); 1652b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1653b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (needBlocking && !waitForThumbnailReady(origUri)) { 165420434e032e498b716f87cce2f23dd646819218bfRay Chen Log.w(TAG, "original media doesn't exist or it's canceled."); 1655b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return false; 165620434e032e498b716f87cce2f23dd646819218bfRay Chen } else if (cancelRequest) { 1657e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen String groupId = uri.getQueryParameter("group_id"); 1658e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen boolean isVideo = "video".equals(uri.getPathSegments().get(1)); 165920434e032e498b716f87cce2f23dd646819218bfRay Chen int pid = Binder.getCallingPid(); 166020434e032e498b716f87cce2f23dd646819218bfRay Chen long id = -1; 1661e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen long gid = -1; 1662e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 166320434e032e498b716f87cce2f23dd646819218bfRay Chen try { 166420434e032e498b716f87cce2f23dd646819218bfRay Chen id = Long.parseLong(origId); 1665e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen gid = Long.parseLong(groupId); 166620434e032e498b716f87cce2f23dd646819218bfRay Chen } catch (NumberFormatException ex) { 166720434e032e498b716f87cce2f23dd646819218bfRay Chen // invalid cancel request 166820434e032e498b716f87cce2f23dd646819218bfRay Chen return false; 166920434e032e498b716f87cce2f23dd646819218bfRay Chen } 1670e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen 167120434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mMediaThumbQueue) { 1672e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen if (mCurrentThumbRequest != null && 1673e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) { 167420434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mCurrentThumbRequest) { 167520434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL; 167620434e032e498b716f87cce2f23dd646819218bfRay Chen mCurrentThumbRequest.notifyAll(); 167720434e032e498b716f87cce2f23dd646819218bfRay Chen } 167820434e032e498b716f87cce2f23dd646819218bfRay Chen } 167920434e032e498b716f87cce2f23dd646819218bfRay Chen for (MediaThumbRequest mtq : mMediaThumbQueue) { 1680e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen if (matchThumbRequest(mtq, pid, id, gid, isVideo)) { 168120434e032e498b716f87cce2f23dd646819218bfRay Chen synchronized (mtq) { 168220434e032e498b716f87cce2f23dd646819218bfRay Chen mtq.mState = MediaThumbRequest.State.CANCEL; 168320434e032e498b716f87cce2f23dd646819218bfRay Chen mtq.notifyAll(); 168420434e032e498b716f87cce2f23dd646819218bfRay Chen } 168520434e032e498b716f87cce2f23dd646819218bfRay Chen 168620434e032e498b716f87cce2f23dd646819218bfRay Chen mMediaThumbQueue.remove(mtq); 168720434e032e498b716f87cce2f23dd646819218bfRay Chen } 168820434e032e498b716f87cce2f23dd646819218bfRay Chen } 168920434e032e498b716f87cce2f23dd646819218bfRay Chen } 1690b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1691b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1692b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (origId != null) { 1693b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen qb.appendWhere(column + " = " + origId); 1694b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1695b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return true; 1696b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1697b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen @SuppressWarnings("fallthrough") 1698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 1699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public Cursor query(Uri uri, String[] projectionIn, String selection, 1700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] selectionArgs, String sort) { 1701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int table = URI_MATCHER.match(uri); 1702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 170301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // Log.v(TAG, "query: uri="+uri+", selection="+selection); 1704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 1705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (table == MEDIA_SCANNER) { 1706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mMediaScannerVolume == null) { 1707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 1708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 1709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create a cursor to return volume currently being scanned by the media scanner 17100027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME}); 17110027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen c.addRow(new String[] {mMediaScannerVolume}); 17120027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen return c; 1713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 17160027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // Used temporarily (until we have unique media IDs) to get an identifier 17170027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // for the current sd card, so that the music app doesn't have to use the 17180027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // non-public getFatVolumeId method 17190027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen if (table == FS_ID) { 17200027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen MatrixCursor c = new MatrixCursor(new String[] {"fsid"}); 17210027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen c.addRow(new Integer[] {mVolumeId}); 17220027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen return c; 17230027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen } 17240027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 1725704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen if (table == VERSION) { 1726704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen MatrixCursor c = new MatrixCursor(new String[] {"version"}); 1727704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen c.addRow(new Integer[] {DATABASE_VERSION}); 1728704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen return c; 1729704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen } 1730704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen 1731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String groupBy = null; 1732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 1733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 1734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 1735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getReadableDatabase(); 17375fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) return null; 1738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 17394574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String limit = uri.getQueryParameter("limit"); 1740c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String filter = uri.getQueryParameter("filter"); 1741c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String [] keywords = null; 1742c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (filter != null) { 1743c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen filter = Uri.decode(filter).trim(); 1744c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (!TextUtils.isEmpty(filter)) { 1745c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String [] searchWords = filter.split(" "); 1746c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen keywords = new String[searchWords.length]; 1747c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen Collator col = Collator.getInstance(); 1748c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen col.setStrength(Collator.PRIMARY); 1749c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; i < searchWords.length; i++) { 1750c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen String key = MediaStore.Audio.keyFor(searchWords[i]); 1751c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("\\", "\\\\"); 1752c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("%", "\\%"); 1753c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen key = key.replace("_", "\\_"); 1754c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen keywords[i] = key; 1755c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1756c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1757c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1758db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin if (uri.getQueryParameter("distinct") != null) { 1759db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin qb.setDistinct(true); 1760db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin } 1761c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen 1762b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen boolean hasThumbnailId = false; 1763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (table) { 1765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 1766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("images"); 1767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (uri.getQueryParameter("distinct") != null) 1768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setDistinct(true); 1769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // set the project map so that data dir is prepended to _data. 1771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project //qb.setProjectionMap(mImagesProjectionMap, true); 1772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 1775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("images"); 1776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (uri.getQueryParameter("distinct") != null) 1777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setDistinct(true); 1778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // set the project map so that data dir is prepended to _data. 1780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project //qb.setProjectionMap(mImagesProjectionMap, true); 1781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id = " + uri.getPathSegments().get(3)); 1782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS_ID: 1785b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen hasThumbnailId = true; 1786b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS: 1787b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) { 1788b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return null; 1789b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 1793d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 1794ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.equalsIgnoreCase("is_music=1") 1795ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen || selection.equalsIgnoreCase("is_podcast=1") ) 1796c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 1797c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 1798ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting songs"); 1799ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 1800ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 1801ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio"); 1802c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 1803c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 1804c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 1805c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1806c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 1807c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 1808c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE '%" + 1809c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen keywords[i] + "%' ESCAPE '\\'"); 1810c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1811ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 1812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 1815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio"); 1816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(3)); 1817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 1820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 1821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id IN (SELECT genre_id FROM " + 1822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_genres_map WHERE audio_id = " + 1823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project uri.getPathSegments().get(3) + ")"); 1824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 1827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 1828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(5)); 1829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 1832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 1833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id IN (SELECT playlist_id FROM " + 1834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_playlists_map WHERE audio_id = " + 1835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project uri.getPathSegments().get(3) + ")"); 1836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 1839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 1840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(5)); 1841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 1844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 1845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 1848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_genres"); 1849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(3)); 1850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 1853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio"); 1854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id IN (SELECT audio_id FROM " + 1855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "audio_genres_map WHERE genre_id = " + 1856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project uri.getPathSegments().get(3) + ")"); 1857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS_ID: 1860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio"); 1861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(5)); 1862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 1865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 1866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 1869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio_playlists"); 1870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(3)); 1871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 1874e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // if simpleQuery is true, we can do a simpler query on just audio_playlists_map 1875e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // we can do this if we have no keywords and our projection includes just columns 1876e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood // from audio_playlists_map 18774382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood boolean simpleQuery = (keywords == null && projectionIn != null 18784382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood && (selection == null || selection.equalsIgnoreCase("playlist_id=?"))); 187997e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen if (projectionIn != null) { 188097e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen for (int i = 0; i < projectionIn.length; i++) { 1881e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood String p = projectionIn[i]; 1882e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (simpleQuery && !(p.equals("audio_id") || 1883e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood p.equals("playlist_id") || p.equals("play_order"))) { 1884e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood simpleQuery = false; 1885e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } 1886e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (p.equals("_id")) { 188797e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen projectionIn[i] = "audio_playlists_map._id AS _id"; 188897e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen } 1889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 1891e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood if (simpleQuery) { 1892e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.setTables("audio_playlists_map"); 1893e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere("playlist_id = " + uri.getPathSegments().get(3)); 1894e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } else { 1895e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.setTables("audio_playlists_map, audio"); 1896e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere("audio._id = audio_id AND playlist_id = " 1897e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood + uri.getPathSegments().get(3)); 1898e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood for (int i = 0; keywords != null && i < keywords.length; i++) { 1899e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere(" AND "); 1900e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 1901e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood "||" + MediaStore.Audio.Media.ALBUM_KEY + 1902e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood "||" + MediaStore.Audio.Media.TITLE_KEY + 1903e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood " LIKE '%" + keywords[i] + "%' ESCAPE '\\'"); 1904e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood } 1905c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 1909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("audio"); 1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(5)); 1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 1914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("video"); 1915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 1917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("video"); 1918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(3)); 1919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1921b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS_ID: 1922b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen hasThumbnailId = true; 1923b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: 1924b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) { 1925b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return null; 1926b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 1927b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 1928b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 1929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS: 1930d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 1931ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.length() == 0) 1932c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 1933c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 1934ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting artists"); 1935ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 1936ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen projectionIn[0] = "count(distinct artist_id)"; 1937ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.appendWhere("is_music=1"); 1938ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 1939ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("artist_info"); 1940c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 1941c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 1942c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 1943c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1944c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 1945c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen " LIKE '%" + keywords[i] + "%' ESCAPE '\\'"); 1946c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1947ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS_ID: 1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("artist_info"); 1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(3)); 1953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ARTISTS_ID_ALBUMS: 1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String aid = uri.getPathSegments().get(3); 1957acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen qb.setTables("audio LEFT OUTER JOIN album_art ON" + 1958acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen " audio.album_id=album_art.album_id"); 1959acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " + 1960acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "artists_albums_map WHERE artist_id = " + 1961acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen aid + ")"); 1962c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 1963c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 1964c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 1965c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 1966c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen " LIKE '%" + keywords[i] + "%' ESCAPE '\\'"); 1967c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1968acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen groupBy = "audio.album_id"; 1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST, 1970acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " + 1971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST); 1972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setProjectionMap(sArtistAlbumsMap); 1973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMS: 1976d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen if (projectionIn != null && projectionIn.length == 1 && selectionArgs == null 1977ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen && (selection == null || selection.length() == 0) 1978c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && projectionIn[0].equalsIgnoreCase("count(*)") 1979c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen && keywords != null) { 1980ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen //Log.i("@@@@", "taking fast path for counting albums"); 1981ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("audio_meta"); 1982ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen projectionIn[0] = "count(distinct album_id)"; 1983ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.appendWhere("is_music=1"); 1984ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } else { 1985ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen qb.setTables("album_info"); 1986c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen for (int i = 0; keywords != null && i < keywords.length; i++) { 1987c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen if (i > 0) { 1988c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(" AND "); 1989c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1990c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY + 1991c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen "||" + MediaStore.Audio.Media.ALBUM_KEY + 1992c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen " LIKE '%" + keywords[i] + "%' ESCAPE '\\'"); 1993c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen } 1994ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen } 1995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 1996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 1997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMS_ID: 1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("album_info"); 1999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("_id=" + uri.getPathSegments().get(3)); 2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMART_ID: 2003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.setTables("album_art"); 2004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("album_id=" + uri.getPathSegments().get(3)); 2005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2007a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_LEGACY: 2008a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen Log.w(TAG, "Legacy media search Uri used. Please update your code."); 2009a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // fall through 2010a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_FANCY: 2011a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen case AUDIO_SEARCH_BASIC: 2012a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen return doAudioSearch(db, qb, uri, projectionIn, selection, selectionArgs, sort, 20134574e03055af60fada50481f2b34e19a687d5866Marco Nelissen table, limit); 2014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 201516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES_ID: 2016e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 2017b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood qb.appendWhere("_id=" + uri.getPathSegments().get(2)); 2018b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // fall through 201916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES: 2020e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 202116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood qb.setTables("files"); 2022b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood break; 2023b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2024e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood case MTP_OBJECT_REFERENCES: 2025e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int handle = Integer.parseInt(uri.getPathSegments().get(2)); 2026e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return getObjectReferences(db, handle); 2027e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 2028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 2029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalStateException("Unknown URL: " + uri.toString()); 2030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 20324d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection, selectionArgs, groupBy, null, sort, limit)); 2033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = qb.query(db, projectionIn, selection, 20344574e03055af60fada50481f2b34e19a687d5866Marco Nelissen selectionArgs, groupBy, null, sort, limit); 2035b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (c != null) { 2037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project c.setNotificationUri(getContext().getContentResolver(), uri); 2038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2039b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return c; 2041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb, 2044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri, String[] projectionIn, String selection, 20454574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String[] selectionArgs, String sort, int mode, 20464574e03055af60fada50481f2b34e19a687d5866Marco Nelissen String limit) { 2047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 204818c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment(); 2049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mSearchString = mSearchString.replaceAll(" ", " ").trim().toLowerCase(); 2050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String [] searchWords = mSearchString.length() > 0 ? 2052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mSearchString.split(" ") : new String[0]; 2053a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String [] wildcardWords = new String[searchWords.length]; 2054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Collator col = Collator.getInstance(); 2055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project col.setStrength(Collator.PRIMARY); 2056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int len = searchWords.length; 2057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < len; i++) { 2058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Because we match on individual words here, we need to remove words 2059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // like 'a' and 'the' that aren't part of the keys. 20603001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen String key = MediaStore.Audio.keyFor(searchWords[i]); 20613001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("\\", "\\\\"); 20623001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("%", "\\%"); 20633001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen key = key.replace("_", "\\_"); 2064a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen wildcardWords[i] = 2065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project (searchWords[i].equals("a") || searchWords[i].equals("an") || 20663001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen searchWords[i].equals("the")) ? "%" : "%" + key + "%"; 2067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2069a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String where = ""; 2070a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen for (int i = 0; i < searchWords.length; i++) { 2071a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (i == 0) { 20723001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen where = "match LIKE ? ESCAPE '\\'"; 2073a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else { 20743001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen where += " AND match LIKE ? ESCAPE '\\'"; 2075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2078a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen qb.setTables("search"); 2079a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen String [] cols; 2080a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen if (mode == AUDIO_SEARCH_FANCY) { 2081a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsFancy; 2082a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else if (mode == AUDIO_SEARCH_BASIC) { 2083a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsBasic; 2084a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen } else { 2085a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen cols = mSearchColsLegacy; 2086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 20874574e03055af60fada50481f2b34e19a687d5866Marco Nelissen return qb.query(db, cols, where, wildcardWords, null, null, null, limit); 2088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String getType(Uri url) 2092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 2093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (URI_MATCHER.match(url)) { 2094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 2095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 2096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS_ID: 2097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 2098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 2099c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood case FILES_ID: 210026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen Cursor c = null; 210126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen try { 210226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c = query(url, MIME_TYPE_PROJECTION, null, null, null); 210326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen if (c != null && c.getCount() == 1) { 210426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.moveToFirst(); 210526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen String mimeType = c.getString(1); 210626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.deactivate(); 210726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen return mimeType; 210826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } 210926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } finally { 211026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen if (c != null) { 211126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen c.close(); 211226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen } 2113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 2117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS: 2118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Images.Media.CONTENT_TYPE; 2119804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen case AUDIO_ALBUMART_ID: 2120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS_ID: 2121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return "image/jpeg"; 2122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 2124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 2125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 2126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Media.CONTENT_TYPE; 2127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 2129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 2130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Genres.CONTENT_TYPE; 2131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 2132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 2133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Genres.ENTRY_CONTENT_TYPE; 2134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 2135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 2136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Playlists.CONTENT_TYPE; 2137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 2138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 2139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Audio.Playlists.ENTRY_CONTENT_TYPE; 2140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 2142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Video.Media.CONTENT_TYPE; 2143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2144804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen throw new IllegalStateException("Unknown URL : " + url); 2145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 2148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Ensures there is a file in the _data column of values, if one isn't 2149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * present a new file is created. 2150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 2151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param initialValues the values passed to insert by the caller 2152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return the new values 2153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 2154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private ContentValues ensureFile(boolean internal, ContentValues initialValues, 2155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String preferredExtension, String directoryName) { 2156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values; 2157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String file = initialValues.getAsString("_data"); 2158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (TextUtils.isEmpty(file)) { 2159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file = generateFileName(internal, preferredExtension, directoryName); 2160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = new ContentValues(initialValues); 2161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("_data", file); 2162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 2163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = initialValues; 2164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!ensureFileExists(file)) { 2167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalStateException("Unable to create new file: " + file); 2168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return values; 2170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2172d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private void sendObjectAdded(long objectHandle) { 217334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 217434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 217534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood try { 217634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService.sendObjectAdded((int)objectHandle); 217734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } catch (RemoteException e) { 217834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Log.e(TAG, "RemoteException in sendObjectAdded", e); 217934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 218034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 2181d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2182d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2183d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2184d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 2185d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood private void sendObjectRemoved(long objectHandle) { 218634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 218734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 218834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood try { 218934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService.sendObjectRemoved((int)objectHandle); 219034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } catch (RemoteException e) { 219134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Log.e(TAG, "RemoteException in sendObjectRemoved", e); 219234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 219334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 2194d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2195d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2196d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 2197d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood 2198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int bulkInsert(Uri uri, ContentValues values[]) { 2200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 2201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == VOLUMES) { 2202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return super.bulkInsert(uri, values); 2203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 2205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 2206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 2207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 2208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getWritableDatabase(); 22105fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 22115fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 22125fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 2213ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2214ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) { 2215ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen return playlistBulkInsert(db, uri, values); 2216e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } else if (match == MTP_OBJECT_REFERENCES) { 2217e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int handle = Integer.parseInt(uri.getPathSegments().get(2)); 2218e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return setObjectReferences(db, handle, values); 2219ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2220ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.beginTransaction(); 2222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int numInserted = 0; 2223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 2224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int len = values.length; 2225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; i < len; i++) { 22265d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood insertInternal(uri, match, values[i]); 2227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project numInserted = len; 2229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.setTransactionSuccessful(); 2230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 2231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.endTransaction(); 2232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 2234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return numInserted; 2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 2238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public Uri insert(Uri uri, ContentValues initialValues) 2239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 22405d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood int match = URI_MATCHER.match(uri); 22415d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood Uri newUri = insertInternal(uri, match, initialValues); 22425d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood // do not signal notification for MTP objects. 22435d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood // we will signal instead after file transfer is successful. 2244e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood if (newUri != null && match != MTP_OBJECTS) { 2245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 2246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return newUri; 2248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2250ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) { 2251ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen DatabaseUtils.InsertHelper helper = 2252ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen new DatabaseUtils.InsertHelper(db, "audio_playlists_map"); 22538b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID); 22548b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID); 22558b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER); 22568b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen long playlistId = Long.parseLong(uri.getPathSegments().get(3)); 2257ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2258ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.beginTransaction(); 2259ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen int numInserted = 0; 2260ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen try { 2261ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen int len = values.length; 2262ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen for (int i = 0; i < len; i++) { 22638b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.prepareForInsert(); 22648b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // getting the raw Object and converting it long ourselves saves 22658b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // an allocation (the alternative is ContentValues.getAsLong, which 22668b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // returns a Long object) 22678b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen long audioid = ((Number) values[i].get( 22688b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue(); 22698b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(audioidcolidx, audioid); 22708b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(playlistididx, playlistId); 22718b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen // convert to int ourselves to save an allocation. 22728b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen int playorder = ((Number) values[i].get( 22738b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue(); 22748b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.bind(playorderidx, playorder); 22758b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen helper.execute(); 2276ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2277ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen numInserted = len; 2278ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.setTransactionSuccessful(); 2279ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } finally { 2280ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen db.endTransaction(); 2281ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen helper.close(); 2282ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2283ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen getContext().getContentResolver().notifyChange(uri, null); 2284ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen return numInserted; 2285ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen } 2286ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen 2287ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood private long insertDirectory(SQLiteDatabase db, String path) { 2288ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood ContentValues values = new ContentValues(); 2289ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION); 2290ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood values.put(FileColumns.DATA, path); 2291ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood values.put(FileColumns.PARENT, getParent(db, path)); 2292f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood File file = new File(path); 2293f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (file.exists()) { 2294f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000); 2295f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood } 2296ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values); 2297ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood sendObjectAdded(rowId); 2298ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood return rowId; 2299ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood } 2300ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 2301b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood private long getParent(SQLiteDatabase db, String path) { 2302b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood int lastSlash = path.lastIndexOf('/'); 2303b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (lastSlash > 0) { 2304b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood String parentPath = path.substring(0, lastSlash); 2305d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood if (parentPath.equals(mExternalStoragePath)) { 2306b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood return 0; 2307b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2308f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood // Use "LIKE" instead of "=" on case insensitive file systems so we do a 2309f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood // case insensitive match when looking for parent directory. 2310f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood String selection = (mCaseInsensitivePaths ? MediaStore.MediaColumns.DATA + " LIKE ?" 2311f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood : MediaStore.MediaColumns.DATA + "=?"); 2312b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood String [] selargs = { parentPath }; 2313f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood Cursor c = db.query("files", null, selection, selargs, null, null, null); 2314b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood try { 2315b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (c == null || c.getCount() == 0) { 2316b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // parent isn't in the database - so add it 2317ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood return insertDirectory(db, parentPath); 2318b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } else { 2319b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood c.moveToFirst(); 2320b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood return c.getLong(0); 2321b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2322b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } finally { 2323b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood if (c != null) c.close(); 2324b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2325b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } else { 2326b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood return 0; 2327b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2328b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood } 2329b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood private long insertFile(DatabaseHelper database, Uri uri, ContentValues initialValues, int mediaType, 2331afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood boolean notify) { 2332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood SQLiteDatabase db = database.getWritableDatabase(); 2333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood ContentValues values = null; 2334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood switch (mediaType) { 2336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_IMAGE: { 2337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = ensureFile(database.mInternal, initialValues, ".jpg", "DCIM/Camera"); 2338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2339afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000); 2340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String data = values.getAsString(MediaColumns.DATA); 2341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (! values.containsKey(MediaColumns.DISPLAY_NAME)) { 2342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeDisplayName(data, values); 2343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeTakenTime(values); 2345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 2346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2348afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_AUDIO: { 2349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // SQLite Views are read-only, so we need to deconstruct this 2350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // insert and do inserts into the underlying tables. 2351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // If doing this here turns out to be a performance bottleneck, 2352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // consider moving this to native code and using triggers on 2353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // the view. 2354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(initialValues); 2355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 23562658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST); 23572658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION); 23582658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen values.remove(MediaStore.Audio.Media.COMPILATION); 23592658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 2360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Insert the artist into the artist table and remove it from 2361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // the input values 2362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Object so = values.get("artist"); 2363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String s = (so == null ? "" : so.toString()); 2364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("artist"); 2365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long artistRowId; 2366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood HashMap<String, Long> artistCache = database.mArtistCache; 2367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String path = values.getAsString("_data"); 2368afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood synchronized(artistCache) { 2369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long temp = artistCache.get(s); 2370afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (temp == null) { 2371afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist", 2372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s, s, path, 0, null, artistCache, uri); 2373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 2374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood artistRowId = temp.longValue(); 2375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2376afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String artist = s; 2378afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2379afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // Do the same for the album field 2380afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood so = values.get("album"); 2381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s = (so == null ? "" : so.toString()); 2382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("album"); 2383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long albumRowId; 2384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood HashMap<String, Long> albumCache = database.mAlbumCache; 2385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood synchronized(albumCache) { 23862658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen int albumhash = 0; 23872658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (albumartist != null) { 23882658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumhash = albumartist.hashCode(); 23892658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else if (compilation != null && compilation.equals("1")) { 23902658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // nothing to do, hash already set 23912658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else { 23922658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumhash = path.substring(0, path.lastIndexOf('/')).hashCode(); 23932658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } 2394afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String cacheName = s + albumhash; 2395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long temp = albumCache.get(cacheName); 2396afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (temp == null) { 2397afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood albumRowId = getKeyIdForName(db, "albums", "album_key", "album", 2398afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s, cacheName, path, albumhash, artist, albumCache, uri); 2399afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 2400afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood albumRowId = temp; 2401afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2402afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 24035d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood 2404afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("artist_id", Integer.toString((int)artistRowId)); 2405afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("album_id", Integer.toString((int)albumRowId)); 2406afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood so = values.getAsString("title"); 2407afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood s = (so == null ? "" : so.toString()); 2408afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("title_key", MediaStore.Audio.keyFor(s)); 2409afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // do a final trim of the title, in case it started with the special 2410afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // "sort first" character (ascii \001) 2411afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove("title"); 2412afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put("title", s.trim()); 2413b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2414afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeDisplayName(values.getAsString("_data"), values); 2415afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 2416afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2417afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2418afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FileColumns.MEDIA_TYPE_VIDEO: { 2419afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = ensureFile(database.mInternal, initialValues, ".3gp", "video"); 2420afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String data = values.getAsString("_data"); 2421afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeDisplayName(data, values); 2422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood computeTakenTime(values); 2423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood break; 2424afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2426afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (values == null) { 2428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(initialValues); 2429afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2430fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood // compute bucket_id and bucket_display_name for all files 2431afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String path = values.getAsString(MediaStore.MediaColumns.DATA); 2432fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood if (path != null) { 2433fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood computeBucketValues(path, values); 2434fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood } 2435fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000); 2436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2437afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long rowId = 0; 2438afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Integer i = values.getAsInteger( 2439afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID); 2440afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (i != null) { 2441afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = i.intValue(); 2442afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values = new ContentValues(values); 2443afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID); 2444afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String title = values.getAsString(MediaStore.MediaColumns.TITLE); 24473572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen if (title == null && path != null) { 2448c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood title = MediaFile.getFileTitle(path); 2449c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2450c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.TITLE, title); 2451c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 2452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE); 2453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Integer formatObject = values.getAsInteger(FileColumns.FORMAT); 2454c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood int format = (formatObject == null ? 0 : formatObject.intValue()); 2455c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (format == 0) { 2456c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (path == null) { 2457c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood // special case device created playlists 2458afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) { 2459c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST); 2460c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood // create a file path for the benefit of MTP 246117ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood path = mExternalStoragePath 2462afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + "/Playlists/" + values.getAsString(Audio.Playlists.NAME); 2463c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(MediaStore.MediaColumns.DATA, path); 2464c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.PARENT, getParent(db, path)); 2465c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } else { 2466c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood Log.e(TAG, "path is null in insertObject()"); 2467c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2468c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } else { 2469c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood format = MediaFile.getFormatCode(path, mimeType); 2470c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2471c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2472c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (format != 0) { 2473c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.FORMAT, format); 2474c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (mimeType == null) { 2475c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood mimeType = MediaFile.getMimeTypeForFormatCode(format); 2476c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2477c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2478c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 2479c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (mimeType == null) { 2480c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood mimeType = MediaFile.getMimeTypeForFile(path); 2481c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2482c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood if (mimeType != null) { 2483c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood values.put(FileColumns.MIME_TYPE, mimeType); 2484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2485afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_NONE) { 2486afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int fileType = MediaFile.getFileTypeForMimeType(mimeType); 2487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (MediaFile.isAudioFileType(fileType)) { 2488afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_AUDIO; 2489afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isVideoFileType(fileType)) { 2490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_VIDEO; 2491afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isImageFileType(fileType)) { 2492afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_IMAGE; 2493afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else if (MediaFile.isPlayListFileType(fileType)) { 2494afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood mediaType = FileColumns.MEDIA_TYPE_PLAYLIST; 2495afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2496afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2497c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2498afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood values.put(FileColumns.MEDIA_TYPE, mediaType); 2499c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood 2500afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (rowId == 0) { 2501afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) { 25023572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen String name = values.getAsString(Audio.Playlists.NAME); 2503282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood if (name == null && path == null) { 2504282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood // MediaScanner will compute the name from the path if we have one 2505afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood throw new IllegalArgumentException( 2506282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood "no name was provided when inserting abstract playlist"); 2507afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2508afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } else { 2509a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu if (path == null) { 2510afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // path might be null for playlists created on the device 2511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // or transfered via MTP 2512afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood throw new IllegalArgumentException( 2513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "no path was provided when inserting new file"); 2514afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 2515e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2516b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2517f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood // make sure modification date and size are set 2518f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (path != null) { 2519f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood File file = new File(path); 2520f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood if (file.exists()) { 2521f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000); 252216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood values.put(FileColumns.SIZE, file.length()); 2523e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 25245d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 2525b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2526afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood Long parent = values.getAsLong(FileColumns.PARENT); 25275d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood if (parent == null) { 2528e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (path != null) { 2529e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long parentId = getParent(db, path); 253016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood values.put(FileColumns.PARENT, parentId); 2531e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 25325d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } else { 253316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood values.put(FileColumns.PARENT, parent); 25345d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 2535b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 2536afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = db.insert("files", FileColumns.DATE_MODIFIED, values); 2537afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId); 2538afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2539afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (rowId != 0 && notify) { 2540afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood sendObjectAdded(rowId); 2541afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood } 25425d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } else { 254316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood db.update("files", values, FileColumns._ID + "=?", 2544afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood new String[] { Long.toString(rowId) }); 25455d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 2546afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood 2547afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood return rowId; 25481717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood } 25491717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 2550e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood private Cursor getObjectReferences(SQLiteDatabase db, int handle) { 255116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood Cursor c = db.query("files", mMediaTableColumns, "_id=?", 2552e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Integer.toString(handle) }, 2553e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 2554e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 2555e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 2556afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood long playlistId = c.getLong(0); 2557afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 2558afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) { 2559e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only support object references for playlist objects 2560e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return null; 2561e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2562e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return db.rawQuery(OBJECT_REFERENCES_QUERY, 2563afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood new String[] { Long.toString(playlistId) } ); 2564e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2565e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 2566e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 2567e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 2568e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2569e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2570e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return null; 2571e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2572e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 2573e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood private int setObjectReferences(SQLiteDatabase db, int handle, ContentValues values[]) { 2574e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // first look up the media table and media ID for the object 2575e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long playlistId = 0; 257616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood Cursor c = db.query("files", mMediaTableColumns, "_id=?", 2577e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Integer.toString(handle) }, 2578e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 2579e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 2580e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 2581afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 2582afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) { 2583e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only support object references for playlist objects 2584e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return 0; 2585e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2586afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood playlistId = c.getLong(0); 2587e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2588e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 2589e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 2590e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 2591e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2592e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2593e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (playlistId == 0) { 2594e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return 0; 2595e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2596e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 2597e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // next delete any existing entries 2598e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood db.delete("audio_playlists_map", "playlist_id=?", 2599e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Long.toString(playlistId) }); 2600e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 2601e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // finally add the new entries 2602e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int count = values.length; 2603e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood int added = 0; 2604e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues[] valuesList = new ContentValues[count]; 2605e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood for (int i = 0; i < count; i++) { 2606e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // convert object ID to audio ID 2607e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long audioId = 0; 2608e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID); 260916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood c = db.query("files", mMediaTableColumns, "_id=?", 2610e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood new String[] { Long.toString(objectId) }, 2611e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood null, null, null); 2612e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood try { 2613e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null && c.moveToNext()) { 2614afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood int mediaType = c.getInt(1); 261550d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) { 2616e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we only allow audio files in playlists, so skip 2617e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood continue; 2618e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2619afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood audioId = c.getLong(0); 2620e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2621e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } finally { 2622e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (c != null) { 2623e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood c.close(); 2624e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2625e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2626e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (audioId != 0) { 2627e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues v = new ContentValues(); 2628e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId); 2629e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId); 2630a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added); 2631a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood valuesList[added++] = v; 2632e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2633e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2634e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood if (added < count) { 2635e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // we weren't able to find everything on the list, so lets resize the array 2636e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood // and pass what we have. 2637e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood ContentValues[] newValues = new ContentValues[added]; 2638e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood System.arraycopy(valuesList, 0, newValues, 0, added); 2639e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood valuesList = newValues; 2640e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2641e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood return playlistBulkInsert(db, 2642e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId), 2643e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood valuesList); 2644e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood } 2645e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 26465d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood private Uri insertInternal(Uri uri, int match, ContentValues initialValues) { 2647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long rowId; 2648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2649d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues); 2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 2651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == MEDIA_SCANNER) { 2652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME); 2653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return MediaStore.getMediaScannerUri(); 2654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri newUri = null; 2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 2658819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood if (database == null && match != VOLUMES && match != MTP_CONNECTED) { 2659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 2661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2662819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null 2663819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood : database.getWritableDatabase()); 2664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 2666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: { 2667afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = insertFile(database, uri, initialValues, FileColumns.MEDIA_TYPE_IMAGE, true); 2668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId( 2670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId); 2671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2675b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // This will be triggered by requestMediaThumbnail (see getThumbnailUri) 2676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_THUMBNAILS: { 2677b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg", 2678b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "DCIM/.thumbnails"); 2679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("thumbnails", "name", values); 2680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Images.Thumbnails. 2682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContentUri(uri.getPathSegments().get(0)), rowId); 2683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2687b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri) 2688b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: { 2689b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg", 2690b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen "DCIM/.thumbnails"); 2691b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen rowId = db.insert("videothumbnails", "name", values); 2692b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (rowId > 0) { 2693b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen newUri = ContentUris.withAppendedId(Video.Thumbnails. 2694b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen getContentUri(uri.getPathSegments().get(0)), rowId); 2695b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2696b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 2697b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2698b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: { 2700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = insertFile(database, uri, initialValues, FileColumns.MEDIA_TYPE_AUDIO, true); 2701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId); 2703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: { 2708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long audioId = Long.parseLong(uri.getPathSegments().get(2)); 2709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 2710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Genres.Members.AUDIO_ID, audioId); 2711ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen rowId = db.insert("audio_genres_map", "genre_id", values); 2712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 2714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: { 2719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long audioId = Long.parseLong(uri.getPathSegments().get(2)); 2720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 2721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Playlists.Members.AUDIO_ID, audioId); 2722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("audio_playlists_map", "playlist_id", 2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values); 2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: { 2731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("audio_genres", "audio_id", initialValues); 2732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId); 2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: { 2739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long genreId = Long.parseLong(uri.getPathSegments().get(3)); 2740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 2741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Genres.Members.GENRE_ID, genreId); 2742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("audio_genres_map", "genre_id", values); 2743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 2745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: { 2750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 2751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000); 2752afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = insertFile(database, uri, values, FileColumns.MEDIA_TYPE_PLAYLIST, true); 2753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId); 2755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 2760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: { 2761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Long playlistId = Long.parseLong(uri.getPathSegments().get(3)); 2762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 2763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId); 2764ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen rowId = db.insert("audio_playlists_map", "playlist_id", values); 2765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 2767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: { 2772afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood rowId = insertFile(database, uri, initialValues, FileColumns.MEDIA_TYPE_VIDEO, true); 2773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2774b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen newUri = ContentUris.withAppendedId(Video.Media.getContentUri( 2775b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen uri.getPathSegments().get(0)), rowId); 2776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2780c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood case AUDIO_ALBUMART: { 2781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database.mInternal) { 2782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("no internal album art allowed"); 2783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = null; 2785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 2786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER); 2787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (IllegalStateException ex) { 2788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // probably no more room to store albumthumbs 2789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values = initialValues; 2790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert("album_art", "_data", values); 2792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 2793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newUri = ContentUris.withAppendedId(uri, rowId); 2794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2796c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood } 2797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VOLUMES: 2799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return attachVolume(initialValues.getAsString("name")); 2800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2801819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood case MTP_CONNECTED: 280234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 280334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService == null) { 280434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood Context context = getContext(); 280534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // MTP is connected, so grab a connection to MtpService 280634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood context.bindService(new Intent(context, MtpService.class), 280734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpServiceConnection, Context.BIND_AUTO_CREATE); 280834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 280934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 2810819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood break; 2811819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood 2812afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood case FILES: 2813fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood rowId = insertFile(database, uri, initialValues, 2814fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood FileColumns.MEDIA_TYPE_NONE, true); 2815fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood if (rowId > 0) { 2816fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId); 2817fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood } 2818fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood break; 2819fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood 2820e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 2821afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood // don't send a notification if the insert originated from MTP 2822fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood rowId = insertFile(database, uri, initialValues, 2823fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood FileColumns.MEDIA_TYPE_NONE, false); 2824afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood if (rowId > 0) { 2825afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId); 28265d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood } 28275d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood break; 28285d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood 2829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 2830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("Invalid URI " + uri); 2831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return newUri; 2834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2836cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen @Override 2837cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 2838cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen throws OperationApplicationException { 2839cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 2840cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // The operations array provides no overall information about the URI(s) being operated 2841cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // on, so begin a transaction for ALL of the databases. 2842cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI); 2843cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI); 2844cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen SQLiteDatabase idb = ihelper.getWritableDatabase(); 2845cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.beginTransaction(); 2846cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen SQLiteDatabase edb = null; 2847cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (ehelper != null) { 2848cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb = ehelper.getWritableDatabase(); 2849cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.beginTransaction(); 2850cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 2851cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen try { 2852cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen ContentProviderResult[] result = super.applyBatch(operations); 2853cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.setTransactionSuccessful(); 2854cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (edb != null) { 2855cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.setTransactionSuccessful(); 2856cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 2857cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // Rather than sending targeted change notifications for every Uri 2858cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // affected by the batch operation, just invalidate the entire internal 2859cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // and external name space. 2860cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen ContentResolver res = getContext().getContentResolver(); 2861cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen res.notifyChange(Uri.parse("content://media/"), null); 2862cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen return result; 2863cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } finally { 2864cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen idb.endTransaction(); 2865cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (edb != null) { 2866cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen edb.endTransaction(); 2867cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 2868cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 2869cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen } 2870cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 2871cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen 28729299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) { 2873b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen synchronized (mMediaThumbQueue) { 2874e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen MediaThumbRequest req = null; 2875e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen try { 2876e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen req = new MediaThumbRequest( 28779299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen getContext().getContentResolver(), path, uri, priority, magic); 2878e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen mMediaThumbQueue.add(req); 2879e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen // Trigger the handler. 2880e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB); 2881e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen msg.sendToTarget(); 2882e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } catch (Throwable t) { 2883e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen Log.w(TAG, t); 2884e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen } 2885b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen return req; 2886b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2887b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 2888b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String generateFileName(boolean internal, String preferredExtension, String directoryName) 2890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 2891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // create a random file 2892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name = String.valueOf(System.currentTimeMillis()); 2893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (internal) { 2895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException("Writing to internal storage is not supported."); 2896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project// return Environment.getDataDirectory() 2897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project// + "/" + directoryName + "/" + name + preferredExtension; 2898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 289917ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood return mExternalStoragePath + "/" + directoryName + "/" + name + preferredExtension; 2900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private boolean ensureFileExists(String path) { 2904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(path); 2905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (file.exists()) { 2906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return true; 2907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 2908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we will not attempt to create the first directory in the path 2909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // (for example, do not create /sdcard if the SD card is not mounted) 2910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int secondSlash = path.indexOf('/', 1); 2911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (secondSlash < 1) return false; 2912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String directoryPath = path.substring(0, secondSlash); 2913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File directory = new File(directoryPath); 2914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!directory.exists()) 2915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return false; 291617ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood file.getParentFile().mkdirs(); 2917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 291817ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood return file.createNewFile(); 2919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch(IOException ioe) { 2920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "File creation failed", ioe); 2921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return false; 2923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final class GetTableAndWhereOutParameter { 2927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String table; 2928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public String where; 2929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 2930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final GetTableAndWhereOutParameter sGetTableAndWhereParam = 2932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new GetTableAndWhereOutParameter(); 2933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private void getTableAndWhere(Uri uri, int match, String userWhere, 2935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project GetTableAndWhereOutParameter out) { 2936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String where = null; 2937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 29389f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin case IMAGES_MEDIA: 2939afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 2940afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE; 29419f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin break; 29429f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin 2943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 2944afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 2945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id = " + uri.getPathSegments().get(3); 2946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2948b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS_ID: 2949b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen where = "_id=" + uri.getPathSegments().get(3); 2950b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case IMAGES_THUMBNAILS: 2951b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen out.table = "thumbnails"; 2952b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 2953b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 2954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 2955afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 2956afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO; 2957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 2960afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 2961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 2962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES: 2965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 2966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3); 2967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_GENRES_ID: 2970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 2971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3) + 2972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND genre_id=" + uri.getPathSegments().get(5); 2973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS: 2976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists"; 2977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3); 2978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID_PLAYLISTS_ID: 2981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists"; 2982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "audio_id=" + uri.getPathSegments().get(3) + 2983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND playlists_id=" + uri.getPathSegments().get(5); 2984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES: 2987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 2988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID: 2991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 2992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 2993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 2995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS: 2996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 2997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "genre_id=" + uri.getPathSegments().get(3); 2998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 2999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_GENRES_ID_MEMBERS_ID: 3001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_genres"; 3002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "genre_id=" + uri.getPathSegments().get(3) + 3003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND audio_id =" + uri.getPathSegments().get(5); 3004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS: 3007afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3008afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST; 3009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID: 3012afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS: 3017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists_map"; 3018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "playlist_id=" + uri.getPathSegments().get(3); 3019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 3022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "audio_playlists_map"; 3023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "playlist_id=" + uri.getPathSegments().get(3) + 3024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project " AND _id=" + uri.getPathSegments().get(5); 3025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_ALBUMART_ID: 3028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.table = "album_art"; 3029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "album_id=" + uri.getPathSegments().get(3); 3030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 3033afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3034afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO; 3035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 3038afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood out.table = "files"; 3039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project where = "_id=" + uri.getPathSegments().get(3); 3040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3042b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS_ID: 3043b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen where = "_id=" + uri.getPathSegments().get(3); 3044b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen case VIDEO_THUMBNAILS: 3045b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen out.table = "videothumbnails"; 3046b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen break; 3047b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 304816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES_ID: 3049e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 30501717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood where = "_id=" + uri.getPathSegments().get(2); 305116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood case FILES: 3052e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS: 305316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood out.table = "files"; 30541717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood break; 30551717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood 3056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown or unsupported URL: " + uri.toString()); 3059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Add in the user requested WHERE clause, if needed 3062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!TextUtils.isEmpty(userWhere)) { 3063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!TextUtils.isEmpty(where)) { 3064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = where + " AND (" + userWhere + ")"; 3065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = userWhere; 3067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project out.where = where; 3070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3071702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 3074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int delete(Uri uri, String userWhere, String[] whereArgs) { 3075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count; 3076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 3077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // handle MEDIA_SCANNER before calling getDatabaseForUri() 3079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (match == MEDIA_SCANNER) { 3080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mMediaScannerVolume == null) { 3081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return 0; 3082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mMediaScannerVolume = null; 3084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return 1; 3085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3087819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood if (match == VOLUMES_ID) { 3088819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood detachVolume(uri); 3089819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood count = 1; 3090819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood } else if (match == MTP_CONNECTED) { 309134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood synchronized (mMtpServiceConnection) { 309234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood if (mMtpService != null) { 309334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // MTP has disconnected, so release our connection to MtpService 309434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood getContext().unbindService(mMtpServiceConnection); 309534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood count = 1; 309634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // mMtpServiceConnection.onServiceDisconnected might not get called, 309734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood // so set mMtpService = null here 309834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood mMtpService = null; 309934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } else { 310034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood count = 0; 310134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 310234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood } 3103819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood } else { 3104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 3105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 3106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3107819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood "Unknown URI: " + uri + " match: " + match); 3108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getWritableDatabase(); 3110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (sGetTableAndWhereParam) { 3112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam); 3113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 311436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood case MTP_OBJECTS: 3115e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood case MTP_OBJECTS_ID: 3116d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood try { 3117d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood // don't send objectRemoved event since this originated from MTP 3118d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood mDisableMtpObjectCallbacks = true; 311936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood count = db.delete("files", sGetTableAndWhereParam.where, whereArgs); 3120d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } finally { 3121d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood mDisableMtpObjectCallbacks = false; 3122d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood } 312336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood break; 3124e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood 3125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.delete(sGetTableAndWhereParam.table, 3127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 3128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 31303631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // Since there are multiple Uris that can refer to the same files 31313631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // and deletes can affect other objects in storage (like subdirectories 31323631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // or playlists) we will notify a change on the entire volume to make 31333631d46b679a64a16918698121916b60d7c86e97Mike Lockwood // sure no listeners miss the notification. 31343631d46b679a64a16918698121916b60d7c86e97Mike Lockwood String volume = uri.getPathSegments().get(0); 31353631d46b679a64a16918698121916b60d7c86e97Mike Lockwood Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume); 31363631d46b679a64a16918698121916b60d7c86e97Mike Lockwood getContext().getContentResolver().notifyChange(notifyUri, null); 3137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return count; 3141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 3144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public int update(Uri uri, ContentValues initialValues, String userWhere, 3145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String[] whereArgs) { 3146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int count; 3147b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues); 3148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int match = URI_MATCHER.match(uri); 3149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 3150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 3151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 3152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Unknown URI: " + uri); 3153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getWritableDatabase(); 3155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (sGetTableAndWhereParam) { 3157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam); 3158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 31591d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // special case renaming directories via MTP. 31601d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // in this case we must update all paths in the database with 31611d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // the directory name as a prefix 31621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID) 31631d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood && initialValues != null && initialValues.size() == 1) { 31641d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood String oldPath = null; 31651d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood String newPath = initialValues.getAsString("_data"); 31661d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // MtpDatabase will rename the directory first, so we test the new file name 31671d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (newPath != null && (new File(newPath)).isDirectory()) { 31681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION, 31691d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood userWhere, whereArgs, null, null, null); 31701d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood try { 31711d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (cursor != null && cursor.moveToNext()) { 31721d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood oldPath = cursor.getString(1); 31731d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 31741d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } finally { 31751d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (cursor != null) cursor.close(); 31761d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 31771d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (oldPath != null) { 31781d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // first rename the row for the directory 31791d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood count = db.update(sGetTableAndWhereParam.table, initialValues, 31801d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood sGetTableAndWhereParam.where, whereArgs); 31811d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (count > 0) { 31821d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood // then update the paths of any files and folders contained in the directory. 31832d114cff87e8cd61123dce29e87d469abae98f6cMike Lockwood Object[] bindArgs = new Object[] {oldPath + "/", newPath + "/"}; 31842d114cff87e8cd61123dce29e87d469abae98f6cMike Lockwood db.execSQL("UPDATE files SET _data=REPLACE(_data, ?1, ?2);", bindArgs); 31851d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 31861d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 31871d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood if (count > 0 && !db.inTransaction()) { 31881d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood getContext().getContentResolver().notifyChange(uri, null); 31891d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 31901d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood return count; 31911d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 31921d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 31931d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood } 31941d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 3195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (match) { 3196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA: 3197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case AUDIO_MEDIA_ID: 3198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 3199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 32002658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST); 32012658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION); 32022658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen values.remove(MediaStore.Audio.Media.COMPILATION); 320307656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen 3204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Insert the artist into the artist table and remove it from 3205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // the input values 3206a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String artist = values.getAsString("artist"); 32076006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen values.remove("artist"); 3208a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (artist != null) { 3209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long artistRowId; 3210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> artistCache = database.mArtistCache; 3211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized(artistCache) { 3212a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen Long temp = artistCache.get(artist); 3213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (temp == null) { 3214acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist", 3215acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen artist, artist, null, 0, null, artistCache, uri); 3216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project artistRowId = temp.longValue(); 3218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("artist_id", Integer.toString((int)artistRowId)); 3221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 322359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // Do the same for the album field. 3224a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String so = values.getAsString("album"); 32256006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen values.remove("album"); 3226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (so != null) { 322759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen String path = values.getAsString("_data"); 322859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen int albumHash = 0; 32292658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen if (albumartist != null) { 32302658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen albumHash = albumartist.hashCode(); 32312658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else if (compilation != null && compilation.equals("1")) { 32322658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // nothing to do, hash already set 32332658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen } else if (path == null) { 323459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // If the path is null, we don't have a hash for the file in question. 323559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen Log.w(TAG, "Update without specified path."); 323659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } else { 323759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen albumHash = path.substring(0, path.lastIndexOf('/')).hashCode(); 323859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 32392658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen 3240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = so.toString(); 3241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long albumRowId; 3242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashMap<String, Long> albumCache = database.mAlbumCache; 3243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized(albumCache) { 324459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen String cacheName = s + albumHash; 324559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen Long temp = albumCache.get(cacheName); 3246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (temp == null) { 3247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project albumRowId = getKeyIdForName(db, "albums", "album_key", "album", 3248a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen s, cacheName, path, albumHash, artist, albumCache, uri); 3249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project albumRowId = temp.longValue(); 3251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("album_id", Integer.toString((int)albumRowId)); 3254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // don't allow the title_key field to be updated directly 3257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove("title_key"); 3258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // If the title field is modified, update the title_key 3259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project so = values.getAsString("title"); 3260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (so != null) { 3261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String s = so.toString(); 3262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.put("title_key", MediaStore.Audio.keyFor(s)); 3263e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen // do a final trim of the title, in case it started with the special 3264e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen // "sort first" character (ascii \001) 3265e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen values.remove("title"); 3266e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen values.put("title", s.trim()); 3267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3269afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood count = db.update(sGetTableAndWhereParam.table, values, 3270afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood sGetTableAndWhereParam.where, whereArgs); 3271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA: 3274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case IMAGES_MEDIA_ID: 3275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA: 3276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case VIDEO_MEDIA_ID: 3277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 3278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues values = new ContentValues(initialValues); 3279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Don't allow bucket id or display name to be updated directly. 3280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // The same names are used for both images and table columns, so 3281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // we use the ImageColumns constants here. 3282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove(ImageColumns.BUCKET_ID); 3283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project values.remove(ImageColumns.BUCKET_DISPLAY_NAME); 3284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // If the data is being modified update the bucket values 3285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String data = values.getAsString(MediaColumns.DATA); 3286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (data != null) { 3287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project computeBucketValues(data, values); 3288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3289b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen computeTakenTime(values); 3290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.update(sGetTableAndWhereParam.table, values, 3291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 329201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // if this is a request from MediaScanner, DATA should contains file path 329301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // we only process update request from media scanner, otherwise the requests 329401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen // could be duplicate. 329501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) { 329601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen Cursor c = db.query(sGetTableAndWhereParam.table, 329701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen READY_FLAG_PROJECTION, sGetTableAndWhereParam.where, 329801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen whereArgs, null, null, null); 3299b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen if (c != null) { 3300216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen try { 3301216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen while (c.moveToNext()) { 3302216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen long magic = c.getLong(2); 3303216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen if (magic == 0) { 3304216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen requestMediaThumbnail(c.getString(1), uri, 3305216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen MediaThumbRequest.PRIORITY_NORMAL, 0); 3306216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } 3307b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3308216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen } finally { 3309216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen c.close(); 3310b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3311b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3312b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen } 3313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3315f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen 3316f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen case AUDIO_PLAYLISTS_ID_MEMBERS_ID: 3317f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen String moveit = uri.getQueryParameter("move"); 3318f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (moveit != null) { 3319f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER; 3320f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (initialValues.containsKey(key)) { 3321f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen int newpos = initialValues.getAsInteger(key); 3322f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen List <String> segments = uri.getPathSegments(); 3323f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen long playlist = Long.valueOf(segments.get(3)); 3324f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen int oldpos = Integer.valueOf(segments.get(5)); 3325f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen return movePlaylistEntry(db, playlist, oldpos, newpos); 3326f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 3327f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen throw new IllegalArgumentException("Need to specify " + key + 3328f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " when using 'move' parameter"); 3329f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 3330f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // fall through 3331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project count = db.update(sGetTableAndWhereParam.table, initialValues, 3333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project sGetTableAndWhereParam.where, whereArgs); 3334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3337cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // in a transaction, the code that began the transaction should be taking 3338cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen // care of notifications once it ends the transaction successfully 3339cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen if (count > 0 && !db.inTransaction()) { 3340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 3341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return count; 3343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3345f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen private int movePlaylistEntry(SQLiteDatabase db, long playlist, int from, int to) { 3346f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (from == to) { 3347f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen return 0; 3348f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 3349f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.beginTransaction(); 3350f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen try { 3351f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen int numlines = 0; 3352f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=-1" + 3353f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " WHERE play_order=" + from + 3354f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 3355f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // We could just run both of the next two statements, but only one of 3356f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // of them will actually do anything, so might as well skip the compile 3357f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen // and execute steps. 3358f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen if (from < to) { 3359f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" + 3360f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " WHERE play_order<=" + to + " AND play_order>" + from + 3361f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 3362f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen numlines = to - from + 1; 3363f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } else { 3364f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" + 3365f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " WHERE play_order>=" + to + " AND play_order<" + from + 3366f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " AND playlist_id=" + playlist); 3367f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen numlines = from - to + 1; 3368f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 3369f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.execSQL("UPDATE audio_playlists_map SET play_order=" + to + 3370f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen " WHERE play_order=-1 AND playlist_id=" + playlist); 3371f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.setTransactionSuccessful(); 3372f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI 3373f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen .buildUpon().appendEncodedPath(String.valueOf(playlist)).build(); 3374f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen getContext().getContentResolver().notifyChange(uri, null); 3375f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen return numlines; 3376f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } finally { 3377f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen db.endTransaction(); 3378f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 3379f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen } 3380f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen 3381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String[] openFileColumns = new String[] { 3382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns.DATA, 3383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 3384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project @Override 3386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project public ParcelFileDescriptor openFile(Uri uri, String mode) 3387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throws FileNotFoundException { 338871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor pfd = null; 339071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 339171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) { 339271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // get album art for the specified media file 339371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen DatabaseHelper database = getDatabaseForUri(uri); 339471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (database == null) { 339571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw new IllegalStateException("Couldn't open database for " + uri); 339671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 339771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen SQLiteDatabase db = database.getReadableDatabase(); 33985fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 33995fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 34005fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 340171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 340271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen int songid = Integer.parseInt(uri.getPathSegments().get(3)); 340371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.setTables("audio_meta"); 340471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.appendWhere("_id=" + songid); 340571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen Cursor c = qb.query(db, 340671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen new String [] { 340771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen MediaStore.Audio.Media.DATA, 340871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen MediaStore.Audio.Media.ALBUM_ID }, 340971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen null, null, null, null, null); 341071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (c.moveToFirst()) { 341171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen String audiopath = c.getString(0); 341271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen int albumid = c.getInt(1); 341371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // Try to get existing album art for this album first, which 341471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // could possibly have been obtained from a different file. 341571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // If that fails, try to get it from this specific file. 341671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid); 341771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen try { 34183fa7593ce394cdaad4a3db622d415fd8497f4a9dMarco Nelissen pfd = openFileHelper(newUri, mode); 341971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } catch (FileNotFoundException ex) { 342071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // That didn't work, now try to get it from the specific file 342171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen pfd = getThumb(db, audiopath, albumid, null); 342271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 342371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 342471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen c.close(); 342571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return pfd; 342671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 342771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 3428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 3429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project pfd = openFileHelper(uri, mode); 3430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (FileNotFoundException ex) { 343171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (mode.contains("w")) { 343271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // if the file couldn't be created, we shouldn't extract album art 343371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw ex; 343471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 343571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 3436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) { 3437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Tried to open an album art file which does not exist. Regenerate. 3438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = getDatabaseForUri(uri); 3439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) { 3440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw ex; 3441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db = database.getReadableDatabase(); 34435fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang if (db == null) { 34445fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang throw new IllegalStateException("Couldn't open database for " + uri); 34455fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang } 3446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 3447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int albumid = Integer.parseInt(uri.getPathSegments().get(3)); 344871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen qb.setTables("audio_meta"); 3449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project qb.appendWhere("album_id=" + albumid); 3450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = qb.query(db, 3451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new String [] { 3452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.Audio.Media.DATA }, 3453a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen null, null, null, null, MediaStore.Audio.Media.TRACK); 345471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (c.moveToFirst()) { 3455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String audiopath = c.getString(0); 345671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen pfd = getThumb(db, audiopath, albumid, uri); 3457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project c.close(); 3459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 346071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (pfd == null) { 346171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen throw ex; 346271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 3463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return pfd; 3465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private class ThumbData { 3468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project SQLiteDatabase db; 3469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String path; 3470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long album_id; 3471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri albumart_uri; 3472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3474a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen private void makeThumbAsync(SQLiteDatabase db, String path, long album_id) { 34758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber synchronized (mPendingThumbs) { 34768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (mPendingThumbs.contains(path)) { 34778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // There's already a request to make an album art thumbnail 34788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // for this audio file in the queue. 34798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return; 34808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 34818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 34828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber mPendingThumbs.add(path); 34838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 34848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ThumbData d = new ThumbData(); 3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.db = db; 3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.path = path; 3488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project d.album_id = album_id; 3489a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id); 34908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 34918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Instead of processing thumbnail requests in the order they were 34928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // received we instead process them stack-based, i.e. LIFO. 34938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // The idea behind this is that the most recently requested thumbnails 34948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // are most likely the ones still in the user's view, whereas those 34958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // requested earlier may have already scrolled off. 34968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber synchronized (mThumbRequestStack) { 34978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber mThumbRequestStack.push(d); 34988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 34998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 35008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Trigger the handler. 3501b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB); 3502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project msg.sendToTarget(); 3503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 35058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Extract compressed image data from the audio file itself or, if that fails, 35068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // look for a file "AlbumArt.jpg" in the containing directory. 35078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private static byte[] getCompressedAlbumArt(Context context, String path) { 35088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber byte[] compressed = null; 3509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 3511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File f = new File(path); 3512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f, 3513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ParcelFileDescriptor.MODE_READ_ONLY); 3514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 35158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber MediaScanner scanner = new MediaScanner(context); 35168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber compressed = scanner.extractAlbumArt(pfd.getFileDescriptor()); 3517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project pfd.close(); 3518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3519d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // If no embedded art exists, look for a suitable image file in the 35203f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen // same directory as the media file, except if that directory is 35213f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen // is the root directory of the sd card or the download directory. 3522d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // We look for, in order of preference: 3523d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 0 AlbumArt.jpg 3524d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 1 AlbumArt*Large.jpg 3525d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 2 Any other jpg image with 'albumart' anywhere in the name 3526d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 3 Any other jpg image 3527d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // 4 any other png image 35288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (compressed == null && path != null) { 3529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project int lastSlash = path.lastIndexOf('/'); 3530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (lastSlash > 0) { 3531d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 35323f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen String artPath = path.substring(0, lastSlash); 353317ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood String sdroot = mExternalStoragePath; 35340fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen String dwndir = Environment.getExternalStoragePublicDirectory( 35352f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); 3536d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 3537d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String bestmatch = null; 3538d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen synchronized (sFolderArtMap) { 3539d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (sFolderArtMap.containsKey(artPath)) { 3540d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = sFolderArtMap.get(artPath); 3541ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen } else if (!artPath.equalsIgnoreCase(sdroot) && 3542ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen !artPath.equalsIgnoreCase(dwndir)) { 3543d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen File dir = new File(artPath); 3544d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String [] entrynames = dir.list(); 3545d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (entrynames == null) { 3546d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen return null; 3547d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 3548d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = null; 3549d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen int matchlevel = 1000; 3550d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen for (int i = entrynames.length - 1; i >=0; i--) { 3551d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen String entry = entrynames[i].toLowerCase(); 3552d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (entry.equals("albumart.jpg")) { 3553d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 3554d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen break; 3555d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.startsWith("albumart") 3556d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && entry.endsWith("large.jpg") 3557d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && matchlevel > 1) { 3558d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 3559d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 1; 3560d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.contains("albumart") 3561d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && entry.endsWith(".jpg") 3562d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen && matchlevel > 2) { 3563d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 3564d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 2; 3565d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.endsWith(".jpg") && matchlevel > 3) { 3566d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 3567d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 3; 3568d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } else if (entry.endsWith(".png") && matchlevel > 4) { 3569d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen bestmatch = entrynames[i]; 3570d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen matchlevel = 4; 3571d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 3572d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 3573d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen // note that this may insert null if no album art was found 3574d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen sFolderArtMap.put(artPath, bestmatch); 3575d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 3576d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 3577d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen 3578d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (bestmatch != null) { 35793f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen File file = new File(artPath, bestmatch); 3580d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (file.exists()) { 3581d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen compressed = new byte[(int)file.length()]; 3582d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen FileInputStream stream = null; 3583d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen try { 3584d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream = new FileInputStream(file); 3585d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream.read(compressed); 3586d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } catch (IOException ex) { 3587d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen compressed = null; 3588d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } finally { 3589d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen if (stream != null) { 3590d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen stream.close(); 3591d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen } 3592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 35978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (IOException e) { 35988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 3599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 36008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return compressed; 36018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 3602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 36038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Return a URI to write the album art to and update the database as necessary. 36048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Uri getAlbumArtOutputUri(SQLiteDatabase db, long album_id, Uri albumart_uri) { 36058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Uri out = null; 36068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // TODO: this could be done more efficiently with a call to db.replace(), which 36078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // replaces or inserts as needed, making it unnecessary to query() first. 36088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (albumart_uri != null) { 36098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Cursor c = query(albumart_uri, new String [] { "_data" }, 36108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber null, null, null); 3611d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson try { 3612d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (c != null && c.moveToFirst()) { 3613d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson String albumart_path = c.getString(0); 3614d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (ensureFileExists(albumart_path)) { 3615d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson out = albumart_uri; 3616d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } 3617d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } else { 3618d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson albumart_uri = null; 3619d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } 3620d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson } finally { 3621d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson if (c != null) { 3622d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson c.close(); 3623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 362571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 362671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (albumart_uri == null){ 36278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber ContentValues initialValues = new ContentValues(); 36288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber initialValues.put("album_id", album_id); 36298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 36308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER); 36318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber long rowId = db.insert("album_art", "_data", values); 36328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (rowId > 0) { 36338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber out = ContentUris.withAppendedId(ALBUMART_URI, rowId); 3634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 36358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (IllegalStateException ex) { 36368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Log.e(TAG, "error creating album thumb file"); 36378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 36388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 36398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber return out; 36408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 36418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 36428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // Write out the album art to the output URI, recompresses the given Bitmap 36438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // if necessary, otherwise writes the compressed data. 36448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber private void writeAlbumArt( 36458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) { 36468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber boolean success = false; 36478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 36488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber OutputStream outstream = getContext().getContentResolver().openOutputStream(out); 36498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 36508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (!need_to_recompress) { 36518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // No need to recompress here, just write out the original 36528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // compressed data here. 36538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber outstream.write(compressed); 36548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber success = true; 36558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } else { 365670676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann success = bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream); 3657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 36588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 36598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber outstream.close(); 36608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (FileNotFoundException ex) { 36618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Log.e(TAG, "error creating file", ex); 3662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (IOException ex) { 36638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Log.e(TAG, "error creating file", ex); 36648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 36658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (!success) { 36668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // the thumbnail was not written successfully, delete the entry that refers to it 36678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber getContext().getContentResolver().delete(out, null, null); 3668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 36698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 36708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 367171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen private ParcelFileDescriptor getThumb(SQLiteDatabase db, String path, long album_id, 367271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen Uri albumart_uri) { 367371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen ThumbData d = new ThumbData(); 367471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.db = db; 367571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.path = path; 367671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.album_id = album_id; 367771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen d.albumart_uri = albumart_uri; 367871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return makeThumbInternal(d); 367971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 368071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen 368171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen private ParcelFileDescriptor makeThumbInternal(ThumbData d) { 36828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber byte[] compressed = getCompressedAlbumArt(getContext(), d.path); 3683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 36848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (compressed == null) { 368571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 36868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 36878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 36888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber Bitmap bm = null; 36898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber boolean need_to_recompress = true; 36908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 36918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber try { 36928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // get the size of the bitmap 36938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber BitmapFactory.Options opts = new BitmapFactory.Options(); 36948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inJustDecodeBounds = true; 36958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inSampleSize = 1; 36968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts); 36978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 36988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // request a reasonably sized output image 369970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann final Resources r = getContext().getResources(); 370070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size); 370170676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) { 37028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.outHeight /= 2; 37038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.outWidth /= 2; 37048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inSampleSize *= 2; 37058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 37068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 37078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (opts.inSampleSize == 1) { 37088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // The original album art was of proper size, we won't have to 37098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // recompress the bitmap later. 37108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber need_to_recompress = false; 37118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } else { 37128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber // get the image for real now 37138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inJustDecodeBounds = false; 37148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber opts.inPreferredConfig = Bitmap.Config.RGB_565; 37158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts); 37168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 37178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (bm != null && bm.getConfig() == null) { 3718a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false); 3719a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (nbm != null && nbm != bm) { 3720a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm.recycle(); 3721a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm = nbm; 3722a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } 37238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 37248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 37258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } catch (Exception e) { 37268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 37278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 37288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber if (need_to_recompress && bm == null) { 372971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 37308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 37318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber 373271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen if (d.albumart_uri == null) { 373371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // this one doesn't need to be saved (probably a song with an unknown album), 373471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen // so stick it in a memory file and return that 373571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen try { 373624001394f571b1f0378840cbf299288e4df10508Bjorn Bringert return ParcelFileDescriptor.fromData(compressed, "albumthumb"); 373771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } catch (IOException e) { 373871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 373971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } else { 3740a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // This one needs to actually be saved on the sd card. 3741a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // This is wrapped in a transaction because there are various things 3742a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // that could go wrong while generating the thumbnail, and we only want 3743a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // to update the database when all steps succeeded. 3744a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.beginTransaction(); 3745a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen try { 3746a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen Uri out = getAlbumArtOutputUri(d.db, d.album_id, d.albumart_uri); 3747a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen 3748a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (out != null) { 3749a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen writeAlbumArt(need_to_recompress, out, compressed, bm); 3750a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen getContext().getContentResolver().notifyChange(MEDIA_URI, null); 3751a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen ParcelFileDescriptor pfd = openFileHelper(out, "r"); 3752a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.setTransactionSuccessful(); 3753a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen return pfd; 3754a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } 3755a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } catch (FileNotFoundException ex) { 3756a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // do nothing, just return null below 3757a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } catch (UnsupportedOperationException ex) { 3758a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen // do nothing, just return null below 3759a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen } finally { 3760a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen d.db.endTransaction(); 3761a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen if (bm != null) { 3762a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen bm.recycle(); 376371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 376471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen } 37658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber } 376671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen return null; 3767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 3770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Look up the artist or album entry for the given name, creating that entry 3771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * if it does not already exists. 3772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param db The database 3773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param table The table to store the key/name pair in. 3774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param keyField The name of the key-column 3775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param nameField The name of the name-column 3776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param rawName The name that the calling app was trying to insert into the database 377759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param cacheName The string that will be inserted in to the cache 377859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param path The full path to the file being inserted in to the audio table 377959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen * @param albumHash A hash to distinguish between different albums of the same name 3780a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen * @param artist The name of the artist, if known 3781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param cache The cache to add this entry to 3782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param srcuri The Uri that prompted the call to this method, used for determining whether this is 3783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * the internal or external database 3784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return The row ID for this artist/album, or -1 if the provided name was invalid 3785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 3786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private long getKeyIdForName(SQLiteDatabase db, String table, String keyField, String nameField, 378759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen String rawName, String cacheName, String path, int albumHash, 3788a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen String artist, HashMap<String, Long> cache, Uri srcuri) { 3789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project long rowId; 3790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rawName == null || rawName.length() == 0) { 379251cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen rawName = MediaStore.UNKNOWN_STRING; 3793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String k = MediaStore.Audio.keyFor(rawName); 3795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (k == null) { 379751cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen // shouldn't happen, since we only get null keys for null inputs 379851cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen Log.e(TAG, "null key", new Exception()); 3799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return -1; 3800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 380259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen boolean isAlbum = table.equals("albums"); 3803e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName); 380459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 38052658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // To distinguish same-named albums, we append a hash. The hash is based 38062658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // on the "album artist" tag if present, otherwise on the "compilation" tag 38072658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen // if present, otherwise on the path. 380859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // Ideally we would also take things like CDDB ID in to account, so 380959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // we can group files from the same album that aren't in the same 381059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // folder, but this is a quick and easy start that works immediately 381159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // without requiring support from the mp3, mp4 and Ogg meta data 381259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen // readers, as long as the albums are in different folders. 3813a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (isAlbum) { 381459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen k = k + albumHash; 3815a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen if (isUnknown) { 3816a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen k = k + artist; 3817a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen } 381859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen } 381959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen 3820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String [] selargs = { k }; 3821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null); 3822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 3824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project switch (c.getCount()) { 3825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case 0: { 3826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // insert new entry into table 3827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues otherValues = new ContentValues(); 3828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project otherValues.put(keyField, k); 3829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project otherValues.put(nameField, rawName); 3830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = db.insert(table, "duration", otherValues); 383159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (path != null && isAlbum && ! isUnknown) { 3832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // We just inserted a new album. Now create an album art thumbnail for it. 3833a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen makeThumbAsync(db, path, rowId); 3834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (rowId > 0) { 3836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = srcuri.toString().substring(16, 24); // extract internal/external 3837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId); 3838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 3839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project case 1: { 3843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Use the existing entry 3844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project c.moveToFirst(); 3845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = c.getLong(0); 3846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Determine whether the current rawName is better than what's 3848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // currently stored in the table, and update the table if it is. 3849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String currentFancyName = c.getString(2); 3850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String bestName = makeBestName(rawName, currentFancyName); 3851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!bestName.equals(currentFancyName)) { 3852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // update the table with the new name 3853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project ContentValues newValues = new ContentValues(); 3854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project newValues.put(nameField, bestName); 3855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null); 3856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = srcuri.toString().substring(16, 24); // extract internal/external 3857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId); 3858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 3859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project default: 3863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // corrupt database 3864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "Multiple entries in table " + table + " for key " + k); 3865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project rowId = -1; 3866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project break; 3867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 3869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (c != null) c.close(); 3870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 387259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen if (cache != null && ! isUnknown) { 387359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen cache.put(cacheName, rowId); 3874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return rowId; 3876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 3879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Returns the best string to use for display, given two names. 3880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Note that this function does not necessarily return either one 3881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * of the provided names; it may decide to return a better alternative 3882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * (for example, specifying the inputs "Police" and "Police, The" will 3883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * return "The Police") 3884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 3885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * The basic assumptions are: 3886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - longer is better ("The police" is better than "Police") 3887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - prefix is better ("The Police" is better than "Police, The") 3888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * - accents are better ("Motörhead" is better than "Motorhead") 3889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 3890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param one The first of the two names to consider 3891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param two The last of the two names to consider 3892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return The actual name to use 3893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 3894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String makeBestName(String one, String two) { 3895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String name; 3896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Longer names are usually better. 3898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (one.length() > two.length()) { 3899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = one; 3900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Names with accents are usually better, and conveniently sort later 3902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) { 3903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = one; 3904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 3905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = two; 3906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Prefixes are better than postfixes. 3910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (name.endsWith(", the") || name.endsWith(",the") || 3911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name.endsWith(", an") || name.endsWith(",an") || 3912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name.endsWith(", a") || name.endsWith(",a")) { 3913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String fix = name.substring(1 + name.lastIndexOf(',')); 3914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project name = fix.trim() + " " + name.substring(0, name.lastIndexOf(',')); 3915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // TODO: word-capitalize the resulting name 3918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return name; 3919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 3923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Looks up the database based on the given URI. 3924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 3925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param uri The requested URI 3926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @returns the database for the given URI 3927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 3928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private DatabaseHelper getDatabaseForUri(Uri uri) { 3929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 3930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (uri.getPathSegments().size() > 1) { 3931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return mDatabases.get(uri.getPathSegments().get(0)); 3932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return null; 3935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 3938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Attach the database for a volume (internal or external). 3939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Does nothing if the volume is already attached, otherwise 3940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * checks the volume ID and sets up the corresponding database. 3941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 3942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}. 3943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @return the content URI of the attached volume. 3944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 3945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Uri attachVolume(String volume) { 3946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) { 3947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new SecurityException( 3948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Opening and closing databases not allowed."); 3949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 3952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (mDatabases.get(volume) != null) { // Already attached 3953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Uri.parse("content://media/" + volume); 3954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 3955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 3956993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Context context = getContext(); 3957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper db; 3958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_VOLUME.equals(volume)) { 3959993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood db = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true); 3960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (EXTERNAL_VOLUME.equals(volume)) { 3961993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (Environment.isExternalStorageRemovable()) { 3962993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood String path = mExternalStoragePath; 3963993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood int volumeID = FileUtils.getFatVolumeId(path); 3964993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID); 3965993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood 3966993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // generate database name based on volume ID 3967993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood String dbName = "external-" + Integer.toHexString(volumeID) + ".db"; 3968993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood db = new DatabaseHelper(context, dbName, false); 3969993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood mVolumeId = volumeID; 3970993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 3971993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // external database name should be EXTERNAL_DATABASE_NAME 3972993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // however earlier releases used the external-XXXXXXXX.db naming 3973993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // for devices without removable storage, and in that case we need to convert 3974993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // to this new convention 3975993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME); 3976993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (!dbFile.exists()) { 3977993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // find the most recent external database and rename it to 3978993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // EXTERNAL_DATABASE_NAME, and delete any other older 3979993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // external database files 3980993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File recentDbFile = null; 3981993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood for (String database : context.databaseList()) { 3982993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (database.startsWith("external-")) { 3983993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood File file = context.getDatabasePath(database); 3984993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile == null) { 3985993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood recentDbFile = file; 3986993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else if (file.lastModified() > recentDbFile.lastModified()) { 3987993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood recentDbFile.delete(); 3988993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood recentDbFile = file; 3989993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 3990993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood file.delete(); 3991993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 3992993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 3993993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 3994993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile != null) { 3995993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood if (recentDbFile.renameTo(dbFile)) { 3996993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Log.d(TAG, "renamed database " + recentDbFile.getName() + 3997993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood " to " + EXTERNAL_DATABASE_NAME); 3998993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } else { 3999993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood Log.e(TAG, "Failed to rename database " + recentDbFile.getName() + 4000993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood " to " + EXTERNAL_DATABASE_NAME); 4001993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // This shouldn't happen, but if it does, continue using 4002993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // the file under its old name 4003993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood dbFile = recentDbFile; 4004993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 4005993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 4006993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME 4007993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 4008993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood db = new DatabaseHelper(context, dbFile.getName(), false); 4009993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood } 4010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else { 4011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException("There is no volume named " + volume); 4012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mDatabases.put(volume, db); 4015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (!db.mInternal) { 4017ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood // create default directories (only happens on first boot) 4018ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood createDefaultFolders(db.getWritableDatabase()); 4019ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood 4020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // clean up stray album art files: delete every file not in the database 402117ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood File[] files = new File(mExternalStoragePath, ALBUM_THUMB_FOLDER).listFiles(); 4022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project HashSet<String> fileSet = new HashSet(); 4023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project for (int i = 0; files != null && i < files.length; i++) { 4024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fileSet.add(files[i].getPath()); 4025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, 4028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null); 4029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 4030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (cursor != null && cursor.moveToNext()) { 4031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project fileSet.remove(cursor.getString(0)); 4032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } finally { 4034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (cursor != null) cursor.close(); 4035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Iterator<String> iterator = fileSet.iterator(); 4038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project while (iterator.hasNext()) { 4039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String filename = iterator.next(); 4040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename); 4041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new File(filename).delete(); 4042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume); 4047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project return Uri.parse("content://media/" + volume); 4048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project /** 4051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Detach the database for a volume (must be external). 4052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Does nothing if the volume is already detached, otherwise 4053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * closes the database and sends a notification to listeners. 4054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * 4055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * @param uri The content URI of the volume, as returned by {@link #attachVolume} 4056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */ 4057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private void detachVolume(Uri uri) { 4058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) { 4059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new SecurityException( 4060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Opening and closing databases not allowed."); 4061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project String volume = uri.getPathSegments().get(0); 4064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (INTERNAL_VOLUME.equals(volume)) { 4065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new UnsupportedOperationException( 4066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "Deleting the internal volume is not allowed"); 4067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } else if (!EXTERNAL_VOLUME.equals(volume)) { 4068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project throw new IllegalArgumentException( 4069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project "There is no volume named " + volume); 4070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4071702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project synchronized (mDatabases) { 4073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project DatabaseHelper database = mDatabases.get(volume); 4074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (database == null) return; 4075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project try { 4077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // touch the database file to show it is most recently used 4078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project File file = new File(database.getReadableDatabase().getPath()); 4079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project file.setLastModified(System.currentTimeMillis()); 4080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } catch (SQLException e) { 4081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project Log.e(TAG, "Can't touch database file", e); 4082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project mDatabases.remove(volume); 4085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project database.close(); 4086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project getContext().getContentResolver().notifyChange(uri, null); 4089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume); 4090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static String TAG = "MediaProvider"; 4093ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer private static final boolean LOCAL_LOGV = false; 4094efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen private static final int DATABASE_VERSION = 306; 4095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String INTERNAL_DATABASE_NAME = "internal.db"; 4096993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood private static final String EXTERNAL_DATABASE_NAME = "external.db"; 4097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // maximum number of cached external databases to keep 4099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int MAX_EXTERNAL_DATABASES = 3; 4100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // Delete databases that have not been used in two months 4102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60) 4103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final long OBSOLETE_DATABASE_DB = 5184000000L; 4104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private HashMap<String, DatabaseHelper> mDatabases; 4106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private Handler mThumbHandler; 4108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // name of the volume currently being scanned by the media scanner (or null) 4110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String mMediaScannerVolume; 4111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 41120027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen // current FAT volume ID 4113993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood private int mVolumeId = -1; 41140027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 4115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final String INTERNAL_VOLUME = "internal"; 4116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static final String EXTERNAL_VOLUME = "external"; 4117268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs"; 4118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project // path for writing contents of in memory temp database 4120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private String mTempDatabasePath; 4121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 41221717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS 412316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood // are stored in the "files" table, so do not renumber them unless you also add 41241717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood // a corresponding database upgrade step for it. 4125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_MEDIA = 1; 4126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_MEDIA_ID = 2; 4127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_THUMBNAILS = 3; 4128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int IMAGES_THUMBNAILS_ID = 4; 4129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA = 100; 4131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID = 101; 4132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_GENRES = 102; 4133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_GENRES_ID = 103; 4134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104; 4135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105; 4136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES = 106; 4137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES_ID = 107; 4138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES_ID_MEMBERS = 108; 4139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_GENRES_ID_MEMBERS_ID = 109; 4140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS = 110; 4141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID = 111; 4142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112; 4143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113; 4144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS = 114; 4145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS_ID = 115; 4146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMS = 116; 4147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMS_ID = 117; 4148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ARTISTS_ID_ALBUMS = 118; 4149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMART = 119; 4150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int AUDIO_ALBUMART_ID = 120; 415171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen private static final int AUDIO_ALBUMART_FILE_ID = 121; 4152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VIDEO_MEDIA = 200; 4154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VIDEO_MEDIA_ID = 201; 4155b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int VIDEO_THUMBNAILS = 202; 4156b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final int VIDEO_THUMBNAILS_ID = 203; 4157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VOLUMES = 300; 4159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int VOLUMES_ID = 301; 4160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4161a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_LEGACY = 400; 4162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_BASIC = 401; 4163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen private static final int AUDIO_SEARCH_FANCY = 402; 4164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final int MEDIA_SCANNER = 500; 4166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 41670027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen private static final int FS_ID = 600; 4168704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen private static final int VERSION = 601; 41690027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 417016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood private static final int FILES = 700; 417116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood private static final int FILES_ID = 701; 4172a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu 4173e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood // Used only by the MTP implementation 4174e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECTS = 702; 4175e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECTS_ID = 703; 4176e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood private static final int MTP_OBJECT_REFERENCES = 704; 4177819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood // UsbReceiver calls insert() and delete() with this URI to tell us 4178819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood // when MTP is connected and disconnected 4179819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood private static final int MTP_CONNECTED = 705; 4180b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 4181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final UriMatcher URI_MATCHER = 4182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project new UriMatcher(UriMatcher.NO_MATCH); 4183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4184b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final String[] ID_PROJECTION = new String[] { 4185b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns._ID 4186b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen }; 4187b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 41881d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood private static final String[] PATH_PROJECTION = new String[] { 41891d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood MediaStore.MediaColumns._ID, 41901d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood MediaStore.MediaColumns.DATA, 41911d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood }; 41921d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood 4193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project private static final String[] MIME_TYPE_PROJECTION = new String[] { 4194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns._ID, // 0 4195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project MediaStore.MediaColumns.MIME_TYPE, // 1 4196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project }; 4197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4198b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen private static final String[] READY_FLAG_PROJECTION = new String[] { 4199b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns._ID, 4200b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen MediaStore.MediaColumns.DATA, 4201b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen Images.Media.MINI_THUMB_MAGIC 4202b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen }; 4203b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen 4204e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood private static final String OBJECT_REFERENCES_QUERY = 4205afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map" 4206afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?" 4207afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER; 4208e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood 4209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project static 4210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project { 4211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA); 4212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID); 4213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS); 4214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID); 4215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA); 4217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID); 4218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES); 4219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID); 4220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS); 4221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID); 4222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES); 4223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID); 4224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS); 4225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/genres/#/members/#", AUDIO_GENRES_ID_MEMBERS_ID); 4226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS); 4227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID); 4228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS); 4229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID); 4230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS); 4231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID); 4232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS); 4233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS); 4234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID); 4235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART); 4236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID); 423771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID); 4238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA); 4240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID); 4241b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS); 4242b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID); 4243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER); 4245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 42460027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen URI_MATCHER.addURI("media", "*/fs_id", FS_ID); 4247704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen URI_MATCHER.addURI("media", "*/version", VERSION); 42480027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen 4249819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED); 4250819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood 4251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*", VOLUMES_ID); 4252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", null, VOLUMES); 4253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project 4254b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood // Used by MTP implementation 425516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood URI_MATCHER.addURI("media", "*/file", FILES); 425616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood URI_MATCHER.addURI("media", "*/file/#", FILES_ID); 4257e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS); 4258e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID); 4259e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES); 4260b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood 4261a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen /** 4262a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen * @deprecated use the 'basic' or 'fancy' search Uris instead 4263a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen */ 4264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY, 4265a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_LEGACY); 4266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*", 4267a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_LEGACY); 4268a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 4269a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // used for search suggestions 4270a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY, 4271a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen AUDIO_SEARCH_BASIC); 4272a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY + 4273a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen "/*", AUDIO_SEARCH_BASIC); 4274a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen 4275a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen // used by the music app's search activity 4276a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY); 4277a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY); 4278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project } 4279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project} 4280