MediaProvider.java revision 24001394f571b1f0378840cbf299288e4df10508
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;
33bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher;
34702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
35ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
360027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
37702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.SQLException;
38702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
39702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
40702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
41702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
42702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
43b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile;
44702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
45b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
46702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
47702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
51ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread;
52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
54702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
55d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException;
56702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
57702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
58702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
60702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
61702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images.ImageColumns;
63b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.provider.MediaStore.MtpObjects;
64b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.provider.MediaStore.MtpObjects.ObjectColumns;
65b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.provider.Mtp;
66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
68702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
69702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
70702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
71702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
72702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.text.Collator;
75cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
76702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
77702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
78702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
79f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
80b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
83702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
84702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
85702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
87702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
88702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
89702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
90702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
91702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
92b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
93b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
94702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
95702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
96d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
1008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
1018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
1028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
1038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
10420434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
10520434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
106b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
107b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
108b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
109b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
111a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
113a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
114a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
121a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
122a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
123a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
124a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
125a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
126a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
127a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
128ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
129ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
130a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
131a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
132a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
133a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
134a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
135a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
136a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
137a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
138a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
139a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
14063f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
141a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
142a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
143a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
144a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
145a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
146a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
147a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
148a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
149a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
15063f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
15163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
152e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
15363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
154a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
155a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
15663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
15763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
158a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1591717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    private static final String[] mMediaTableColumns = new String[] {
1601717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood            ObjectColumns._ID,
1611717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood            ObjectColumns.MEDIA_TABLE,
1621717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood            ObjectColumns.MEDIA_ID,
1631717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    };
1641717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
165a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
166a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Remove the external volume and then notify all cursors backed by
172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // data on that volume
173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                detachVolume(Uri.parse("content://media/external"));
174d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                sFolderArtMap.clear();
175b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                MiniThumbFile.reset();
176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
180d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    // set to disable sending events when the operation originates from MTP
181d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private boolean mDisableMtpObjectCallbacks;
182d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
183d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final SQLiteDatabase.CustomFunction mObjectRemovedCallback =
184d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                new SQLiteDatabase.CustomFunction() {
185d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void callback(String[] args) {
186d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // do nothing if the operation originated from MTP
187d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mDisableMtpObjectCallbacks) return;
188d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
189d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "object removed " + args[0]);
190d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            IMtpService mtpService = mMtpService;
191d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mtpService != null) {
192d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                try {
193d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectRemoved(Integer.parseInt(args[0]));
194d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                } catch (NumberFormatException e) {
195d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e);
196d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
197d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
198d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
199d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
200d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
206d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final class DatabaseHelper extends SQLiteOpenHelper {
207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public DatabaseHelper(Context context, String name, boolean internal) {
215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            super(context, name, null, DATABASE_VERSION);
216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, 0, DATABASE_VERSION);
226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, oldV, newV);
235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Touch this particular database and garbage collect old databases.
239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
246d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback);
247d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
308d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private IMtpService mMtpService;
309d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
310d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
311d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood         public void onServiceConnected(ComponentName className, android.os.IBinder service) {
312d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "mMtpService connected");
313d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            mMtpService = IMtpService.Stub.asInterface(service);
314d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
315d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
316d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void onServiceDisconnected(ComponentName className) {
317d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "mMtpService disconnected");
318d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            mMtpService = null;
319d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
320d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
321d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
324d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        final Context context = getContext();
325d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
326acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
327acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums._ID);
328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
330acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
332acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
337acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
338acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
339acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
340acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.ALBUM_ART);
341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
34263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
34363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
344d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "%1", context.getString(R.string.artist_label));
345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
350d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.registerReceiver(mUnmountReceiver, iFilter);
351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
359ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
360ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
361ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
364b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
365b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
36620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
367b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
36820434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
369b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
370b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
371b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
37220434e032e498b716f87cce2f23dd646819218bfRay Chen                            File origFile = new File(mCurrentThumbRequest.mPath);
3734d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
37420434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.execute();
3754d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
3764d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
3774d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
37820434e032e498b716f87cce2f23dd646819218bfRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
3794d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
3804d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
381b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
3821d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
3831d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
3841d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
3851d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
3861d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
387b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
38820434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
38920434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
39020434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
391b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
392b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
393b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
394b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
395b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
396b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
397b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
398b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
3998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
400b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    makeThumbInternal(d);
401b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
402b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
403b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
4048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
408d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.bindService(new Intent(context, MtpService.class),
409d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDatabase(SQLiteDatabase db, boolean internal,
422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (toVersion != DATABASE_VERSION) {
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " +
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    DATABASE_VERSION);
428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
43095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
435d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag.
436acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        // We can't downgrade from those revisions, so start over.
437022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // (the initial change to do this was wrong, so now we actually need to start over
438022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // if the database version is 84-89)
439bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair.
440bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // However version 91 was reused in a divergent development path for gingerbread,
441bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // so we need to support upgrades from 91.
442bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Therefore we will only force a reset for versions 92 - 94.
443d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) ||
444bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    (fromVersion >= 92 && fromVersion <= 94)) {
445acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            fromVersion = 63;
446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
471cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
4729ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup");
4739ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup");
4749ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup");
4759ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup");
476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
506b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
526216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                       "_data TEXT UNIQUE NOT NULL," +
527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
56695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
572acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album) AS number_of_albums, " +
573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
586acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
587acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
588acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
589acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
590acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contiains mappings between audio genres and audio files
609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
699acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
700acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.0 shipped with database version 64
701acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
702acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
708403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // In version 66, originally we updateBucketNames(db, "images"),
709403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // but we need to do it in version 89 and therefore save the update here.
710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
721403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
722403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // In version 68, originally we updateBucketNames(db, "video"),
723403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // but we need to do it in version 89 and therefore save the update here.
724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
73495ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
744e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
745e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
74895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
76395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
764acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
765acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.5 shipped with database version 72
766acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
767acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
7688d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
7698d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
7708d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
7718d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
7728d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
7738d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
7748d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
7758d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
7768d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
7778d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
7788d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
7798d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
7808d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
7818d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
7828d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
783a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
784a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
785a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
786a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
787a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
788a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
789a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
790a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
791a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
792a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
793a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
794a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
795a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
796a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
797a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
798a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
799a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
800a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
801a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
802a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
803a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
804a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
805e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
806a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
807a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
808a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
809a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
810a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
811a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
812a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
813a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
814a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
815a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
816a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
817a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
818a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
819e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
820a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
821a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
822a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
823a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
824a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
825a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
826a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
827a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
828a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
829a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
830a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
831a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
832a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
833a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
834a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
835a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
836a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
83759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
83859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
83995ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
84059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
84159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
84259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
84359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
84415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
84515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
84615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
84715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
84815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
84915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
85015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
85115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
85215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
85315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
85415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
85515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
85615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
85715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
858b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
859acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
860acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.6 shipped with database version 76
861acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
862acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
863b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
864b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
865b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
866b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
867b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
868b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
869b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
870b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
871b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
872b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
873b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
874b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
875b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
876b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
877b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
878b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
879b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
880b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
8811769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
882acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
883acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.0 and 2.0.1 shipped with database version 77
884acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
885acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
8861769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
887044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
8881769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
8891769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
8901769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
891268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
892acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
893acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.1 shipped with database version 78
894acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
895acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
896268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
897268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
898268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
899268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
900268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
901268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            String storageroot = Environment.getExternalStorageDirectory().getAbsolutePath();
902268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            String oldthumbspath = storageroot + "/albumthumbs";
903268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            String newthumbspath = storageroot + "/" + ALBUM_THUMB_FOLDER;
904268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
905268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
906268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
907268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
908268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
909268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
910268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
911268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
912268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
913268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
914268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
915268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
916044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
917044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
918044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
919044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
920044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
9210ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
922166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen        if (fromVersion < 81 && !internal) {
9230ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete entries starting with /mnt/sdcard. This is for the benefit
9240ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // of users running builds between 2.0.1 and 2.1 final only, since
9250ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // users updating from 2.0 or earlier will not have such entries.
9260ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
9270ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // First we need to update the _data fields in the affected tables, since
9280ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // otherwise deleting the entries will also delete the underlying files
9290ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // (via a trigger), and we want to keep them.
9300ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
9310ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
9320ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
9330ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
9340ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
9350ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
936216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
9370ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Once the paths have been renamed, we can safely delete the entries
9380ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';");
9390ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM images WHERE _data IS '////';");
9400ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM video WHERE _data IS '////';");
9410ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';");
9420ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';");
9430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_meta WHERE _data  IS '////';");
9440ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data  IS '////';");
9450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
9460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // rename existing entries starting with /sdcard to /mnt/sdcard
9470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta" +
9480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
9490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists" +
9500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
9510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images" +
9520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
9530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video" +
9540ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
9550ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails" +
9560ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
9570ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails" +
9580ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
9590ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art" +
9600ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
9610ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
9620ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete albums and artists, then clear the modification time on songs, which
9630ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // will cause the media scanner to rescan everything, rebuilding the artist and
9640ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // album tables along the way, while preserving playlists.
9650ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // We need this rescan because ICU also changed, and now generates different
9660ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // collation keys
9670ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from albums");
9680ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from artists");
9690ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
9700ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen        }
97184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen
97284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        if (fromVersion < 82) {
973acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // recreate this view with the correct "group by" specifier
97484403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("DROP VIEW IF EXISTS artist_info");
97584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
97684403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "SELECT artist_id AS _id, artist, artist_key, " +
977acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album_key) AS number_of_albums, " +
97884403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
97984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "GROUP BY artist_key;");
98084403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        }
981216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
982acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /* we skipped over version 83, and reverted versions 84, 85 and 86 */
983ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen
984ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        if (fromVersion < 87) {
985ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // The fastscroll thumb needs an index on the strings being displayed,
986ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // otherwise the queries it does to determine the correct position
987ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // becomes really inefficient
988022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);");
989022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);");
990022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);");
991ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        }
992216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
993acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        if (fromVersion < 88) {
994acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // Clean up a few more things from versions 84/85/86, and recreate
995acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // the few things worth keeping from those changes.
996acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update1;");
997acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update2;");
998acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update3;");
999acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update4;");
1000acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update1;");
1001acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update2;");
1002acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update3;");
1003acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update4;");
1004acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP VIEw IF EXISTS album_artists;");
1005acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);");
1006acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);");
1007acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
1008acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
1009acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
1010acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
1011acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        }
1012403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1013403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        if (fromVersion < 89) {
1014403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            updateBucketNames(db, "images");
1015403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            updateBucketNames(db, "video");
1016403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        }
1017b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1018b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (fromVersion < 91) {
1019b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            // A table containing information for all files,
1020b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            // needed for MTP support
1021b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            db.execSQL("CREATE TABLE IF NOT EXISTS objects (" +
1022b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                       "_id INTEGER PRIMARY KEY," +
1023b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                       "_data TEXT NOT NULL," +
1024b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                       "_size INTEGER," +
1025b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                       "format INTEGER," +
1026b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                       "parent INTEGER," +
1027cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood                       "date_modified INTEGER," +
1028d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                       // ID of the media table for this object.
1029d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                       // Possible values are IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA
1030d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                       // and AUDIO_PLAYLISTS.
1031cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood                       "media_table INTEGER," +
1032d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                       // The row number of this object in the corresponding media table.
1033d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                       "media_id INTEGER" +
1034b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                       ");");
10359ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood
1036d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // Add cross reference from media table to MTP object table
1037d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // Object ID is the row number of this object in the "objects" table
1038d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            db.execSQL("ALTER TABLE images ADD COLUMN object_id INTEGER;");
1039d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            db.execSQL("ALTER TABLE audio_meta ADD COLUMN object_id INTEGER;");
1040d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            db.execSQL("ALTER TABLE video ADD COLUMN object_id INTEGER;");
1041d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (!internal) {
1042d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                // audio_playlists does not exist in internal database
1043d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                db.execSQL("ALTER TABLE audio_playlists ADD COLUMN object_id INTEGER;");
1044d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
1045d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                // cleans up objects table when an image file is deleted
1046d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS images_objects_cleanup DELETE ON images " +
1047d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "BEGIN " +
1048d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "DELETE FROM objects WHERE _id = old.object_id;" +
1049d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "SELECT _OBJECT_REMOVED(old.object_id);" +
1050d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "END");
10519ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood
1052d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                // cleans up objects table when an audio file is deleted
1053d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_objects_cleanup DELETE ON audio_meta " +
1054d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "BEGIN " +
1055d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "DELETE FROM objects WHERE _id = old.object_id;" +
1056d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "SELECT _OBJECT_REMOVED(old.object_id);" +
1057d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "END");
10589ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood
1059d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                // cleans up objects table when a video file is deleted
1060d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS video_objects_cleanup DELETE ON video " +
1061d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "BEGIN " +
1062d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "DELETE FROM objects WHERE _id = old.object_id;" +
1063d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "SELECT _OBJECT_REMOVED(old.object_id);" +
1064d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "END");
10659ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood
1066d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                // cleans up objects table when a playlist file is deleted
1067d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS playlists_objects_cleanup DELETE ON audio_playlists " +
1068d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "BEGIN " +
1069d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "DELETE FROM objects WHERE _id = old.object_id;" +
1070d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            "SELECT _OBJECT_REMOVED(old.object_id);" +
1071d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "END");
1072d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
10739ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood        }
1074bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1075bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // This was actually added in version 91 in gingerbread, but during post-gingerbread development
1076bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // it was not added until version 96.
1077bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        if (fromVersion < 96) {
1078bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // Never query by mini_thumb_magic_index
1079bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index");
1080bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1081bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // sort the items by taken date in each bucket
1082bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)");
1083bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)");
1084bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        }
1085bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1086acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sanityCheck(db, fromVersion);
10871d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    }
10881d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen
10891d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    /**
1090216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * Perform a simple sanity check on the database. Currently this tests
1091216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * whether all the _data entries in audio_meta are unique
1092216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     */
1093216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen    private static void sanityCheck(SQLiteDatabase db, int fromVersion) {
1094216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c1 = db.query("audio_meta", new String[] {"count(*)"},
1095216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1096216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"},
1097216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1098216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.moveToFirst();
1099216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.moveToFirst();
1100216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num1 = c1.getInt(0);
1101216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num2 = c2.getInt(0);
1102216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.close();
1103216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.close();
1104216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        if (num1 != num2) {
1105216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            Log.e(TAG, "audio_meta._data column is not unique while upgrading" +
1106216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                    " from schema " +fromVersion + " : " + num1 +"/" + num2);
1107216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            // Delete all audio_meta rows so they will be rebuilt by the media scanner
1108216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("DELETE FROM audio_meta;");
1109216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        }
1110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
1113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
1114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note that views are read-only, so we define a trigger to allow deletes.
1115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
1116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
1117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
1118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
1119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
1120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
1122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "BEGIN " +
1123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_meta where _id=old._id;" +
1124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_playlists_map where audio_id=old._id;" +
1125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_genres_map where audio_id=old._id;" +
1126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "END");
1127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
112895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the bucket_id and
1131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * bucket_display_name columns are correct.
1132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateBucketNames(SQLiteDatabase db, String tableName) {
1136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
1137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
1140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
1146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ContentValues values = new ContentValues();
1147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    computeBucketValues(data, values);
1148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    int rowId = cursor.getInt(idColumnIndex);
1149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    db.update(tableName, values, "_id=" + rowId, null);
1150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
1162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
1163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
1167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
1168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
1171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
1176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
1177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
1179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
1180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
1181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
1182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
1183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
1184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
1185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
1198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
1202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
1203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
1204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
1205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
1208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
1209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
1210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
1211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
1212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
1214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
1215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
1216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_ID, path.hashCode());
1217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
1218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
1223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
1226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
1227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
1228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
1229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
1230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
1232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1234b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
1235498b62c2912302a23532c73a028a7684c5df33caRay Chen     * Copy taken time from date_modified if we lost the original value (e.g. after factory reset)
1236498b62c2912302a23532c73a028a7684c5df33caRay Chen     * This works for both video and image tables.
1237b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     *
1238b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     * @param values the content values, where taken time is updated.
1239b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     */
1240b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    private static void computeTakenTime(ContentValues values) {
1241b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        if (! values.containsKey(Images.Media.DATE_TAKEN)) {
1242b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // This only happens when MediaScanner finds an image file that doesn't have any useful
1243b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // reference to get this value. (e.g. GPSTimeStamp)
1244498b62c2912302a23532c73a028a7684c5df33caRay Chen            Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED);
1245b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            if (lastModified != null) {
1246b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                values.put(Images.Media.DATE_TAKEN, lastModified * 1000);
1247b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            }
1248b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        }
1249b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    }
1250b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen
1251b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    /**
1252b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
1253b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
1254b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
1255b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
1256b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
1257b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
1258b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
1259b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
1260b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
1261b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1262b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
1263b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1264b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
1265e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen            long id = c.getLong(0);
1266b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
1267b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
1268b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
12699299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            MediaThumbRequest req = requestMediaThumbnail(path, origUri,
12709299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    MediaThumbRequest.PRIORITY_HIGH, magic);
12719299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            if (req == null) {
12729299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                return false;
12739299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            }
12749299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            synchronized (req) {
12759299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                try {
12769299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    while (req.mState == MediaThumbRequest.State.WAIT) {
12779299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        req.wait();
127820434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
12799299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                } catch (InterruptedException e) {
12809299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    Log.w(TAG, e);
12819299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                }
12829299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                if (req.mState == MediaThumbRequest.State.DONE) {
12839299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    result = true;
1284b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1285b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
1286b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1287b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
1288b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1289b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
1290b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1291b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1292e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
1293e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
1294e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
1295e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
1296e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
1297e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
1298e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
1299e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
1300e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
1301e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
1302b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
1303b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
1304b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
1305b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
1306b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
1307b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
1308b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1309b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
1310b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1311b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1312b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
1313b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
1314b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
1315b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
1316b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1317b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
132020434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
1321e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
1322e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
1323e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
1324b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1325b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
132620434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
1327b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
132820434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
1329e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
1330e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
133120434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
133220434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
1333e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
1334e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
133520434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
133620434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
1337e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
133820434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
133920434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
134020434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
134120434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1342e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
134320434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
1344e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
1345e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
134620434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
134720434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
134820434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
134920434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
135020434e032e498b716f87cce2f23dd646819218bfRay Chen                }
135120434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
1352e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
135320434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
135420434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
135520434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
135620434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
135720434e032e498b716f87cce2f23dd646819218bfRay Chen
135820434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
135920434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
136020434e032e498b716f87cce2f23dd646819218bfRay Chen                }
136120434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1362b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1363b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1364b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
1365b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
1366b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1367b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
1368b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1369b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
1370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
1372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
1373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
1374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
137501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
1376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
1378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
1379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
1380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
1381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
13820027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
13830027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
13840027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
1385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
13880027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
13890027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
13900027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
13910027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
13920027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
13930027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
13940027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
13950027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
13960027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
1397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
1398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
1400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
1401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getReadableDatabase();
1403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
14044574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
1405b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
1406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
1408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
1428b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
1429b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
1430b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
1431b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
1432b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1436d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
1437ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
1438ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                          || selection.equalsIgnoreCase("is_podcast=1") )
1439ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)") ) {
1440ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting songs");
1441ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
1442ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
1443ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio");
1444ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
1445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
1455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_genres_map WHERE audio_id = " +
1456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
1467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_playlists_map WHERE audio_id = " +
1468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT audio_id FROM " +
1488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_genres_map WHERE genre_id = " +
1489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
150797e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
150897e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
150997e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        if (projectionIn[i].equals("_id")) {
151097e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
151197e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
1512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists_map, audio");
1515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("audio._id = audio_id AND playlist_id = "
1516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        + uri.getPathSegments().get(3));
1517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
1526bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                if (uri.getQueryParameter("distinct") != null) {
1527bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    qb.setDistinct(true);
1528bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                }
1529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
1531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
1532bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                if (uri.getQueryParameter("distinct") != null) {
1533bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    qb.setDistinct(true);
1534bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                }
1535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1538b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
1539b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
1540b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
1541b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
1542b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
1543b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1544b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1545b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
1547d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
1548ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
1549ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)") ) {
1550ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting artists");
1551ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
1552ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct artist_id)";
1553ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
1554ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
1555ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("artist_info");
1556ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
1557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
1560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
1561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
1565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
1566acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
1567acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        " audio.album_id=album_art.album_id");
1568acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
1569acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "artists_albums_map WHERE artist_id = " +
1570acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                         aid + ")");
1571acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                groupBy = "audio.album_id";
1572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
1573acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
1574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
1575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
1576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
1579d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
1580ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
1581ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)") ) {
1582ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting albums");
1583ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
1584ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct album_id)";
1585ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
1586ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
1587ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("album_info");
1588ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
1589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
1592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
1593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
1597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
1598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + uri.getPathSegments().get(3));
1599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1601a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
1602a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
1603a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
1604a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
1605a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
1606a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection, selectionArgs, sort,
16074574e03055af60fada50481f2b34e19a687d5866Marco Nelissen                        table, limit);
1608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1609b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            case MTP_OBJECTS_ID:
1610b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                qb.appendWhere("_id=" + uri.getPathSegments().get(2));
1611b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                // fall through
1612b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            case MTP_OBJECTS:
1613b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                qb.setTables("objects");
1614b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                break;
1615b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1616e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            case MTP_OBJECT_REFERENCES:
1617e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int handle = Integer.parseInt(uri.getPathSegments().get(2));
1618e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return getObjectReferences(db, handle);
1619e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
1620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
1621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
1622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
16244d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection, selectionArgs, groupBy, null, sort, limit));
1625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
16264574e03055af60fada50481f2b34e19a687d5866Marco Nelissen                selectionArgs, groupBy, null, sort, limit);
1627b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
1629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            c.setNotificationUri(getContext().getContentResolver(), uri);
1630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
1633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
1636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
16374574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
16384574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
1639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
164018c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen        String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment();
1641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
1642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
1644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
1645a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
1646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Collator col = Collator.getInstance();
1647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        col.setStrength(Collator.PRIMARY);
1648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
1649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
1650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
1651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
16523001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            String key = MediaStore.Audio.keyFor(searchWords[i]);
16533001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("\\", "\\\\");
16543001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("%", "\\%");
16553001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("_", "\\_");
1656a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
1657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
16583001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                        searchWords[i].equals("the")) ? "%" : "%" + key + "%";
1659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1661a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
1662a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
1663a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
16643001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where = "match LIKE ? ESCAPE '\\'";
1665a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
16663001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where += " AND match LIKE ? ESCAPE '\\'";
1667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1670a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
1671a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
1672a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
1673a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
1674a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
1675a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
1676a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
1677a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
1678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
16794574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
1680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
1684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
1685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
1686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
169126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                Cursor c = null;
169226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                try {
169326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    c = query(url, MIME_TYPE_PROJECTION, null, null, null);
169426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null && c.getCount() == 1) {
169526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.moveToFirst();
169626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        String mimeType = c.getString(1);
169726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.deactivate();
169826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        return mimeType;
169926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
170026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                } finally {
170126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null) {
170226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.close();
170326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
1704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
1709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
1710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
1711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
1712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
1716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
1717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
1721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
1724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
1727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
1730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
1733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        throw new IllegalStateException("Unknown URL");
1735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
1739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * present a new file is created.
1740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
1742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
1743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
1745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
1746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
1747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String file = initialValues.getAsString("_data");
1748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
1749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
1750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
1751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values.put("_data", file);
1752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
1754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!ensureFileExists(file)) {
1757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalStateException("Unable to create new file: " + file);
1758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
1760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1762d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectAdded(long objectHandle) {
1763d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        IMtpService mtpService = mMtpService;
1764d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (mtpService != null) {
1765d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            try {
1766d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                mtpService.sendObjectAdded((int)objectHandle);
1767d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            } catch (RemoteException e) {
1768d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                Log.e(TAG, "RemoteException in sendObjectAdded", e);
1769d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
1770d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
1771d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
1772d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
1773d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectRemoved(long objectHandle) {
1774d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        IMtpService mtpService = mMtpService;
1775d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (mtpService != null) {
1776d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            try {
1777d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                mtpService.sendObjectRemoved((int)objectHandle);
1778d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            } catch (RemoteException e) {
1779d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                Log.e(TAG, "RemoteException in sendObjectRemoved", e);
1780d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
1781d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
1782d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
1783d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
1784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
1786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
1788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
1789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
1792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
1793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
1794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getWritableDatabase();
1796ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1797ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
1798ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
1799e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } else if (match == MTP_OBJECT_REFERENCES) {
1800e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            int handle = Integer.parseInt(uri.getPathSegments().get(2));
1801e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return setObjectReferences(db, handle, values);
1802ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
1803ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
1806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
1808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
18095d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                insertInternal(uri, match, values[i]);
1810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
1812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
1817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
1818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Uri insert(Uri uri, ContentValues initialValues)
1822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
18235d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int match = URI_MATCHER.match(uri);
18245d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        Uri newUri = insertInternal(uri, match, initialValues);
18255d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // do not signal notification for MTP objects.
18265d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // we will signal instead after file transfer is successful.
18275d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        if (newUri != null && match != MTP_OBJECTS) {
1828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
1829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
1831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1833ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
1834ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
1835ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
18368b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
18378b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
18388b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
18398b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
1840ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1841ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
1842ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
1843ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
1844ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
1845ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
18468b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
18478b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
18488b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
18498b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
18508b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
18518b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
18528b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
18538b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
18548b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
18558b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
18568b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
18578b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
18588b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
1859ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
1860ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
1861ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
1862ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
1863ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
1864ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
1865ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
1866ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
1867ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
1868ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
1869ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1870b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    private long getParent(SQLiteDatabase db, String path) {
1871b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        int lastSlash = path.lastIndexOf('/');
1872b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (lastSlash > 0) {
1873b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String parentPath = path.substring(0, lastSlash);
1874b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            if (parentPath.equals(Environment.getExternalStorageDirectory().getAbsolutePath())) {
1875b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                return 0;
1876b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
1877b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String [] selargs = { parentPath };
1878b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            Cursor c = db.query("objects", null, MediaStore.MediaColumns.DATA + "=?",
1879b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                            selargs, null, null, null);
1880b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            try {
1881b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c == null || c.getCount() == 0) {
1882b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    // parent isn't in the database - so add it
1883b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    ContentValues values = new ContentValues();
18841717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                    values.put(ObjectColumns.FORMAT, Mtp.Object.FORMAT_ASSOCIATION);
18851717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                    values.put(ObjectColumns.DATA, parentPath);
18861717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                    values.put(ObjectColumns.PARENT, getParent(db, parentPath));
1887d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    long parent = db.insert("objects", ObjectColumns.DATE_MODIFIED, values);
1888d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectAdded(parent);
1889d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    return parent;
1890b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                } else {
1891b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    c.moveToFirst();
1892b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    return c.getLong(0);
1893b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                }
1894b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            } finally {
1895b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c != null) c.close();
1896b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
1897b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        } else {
1898b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            return 0;
1899b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
1900b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
1901b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1902d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private long insertObject(SQLiteDatabase db, long objectHandle, ContentValues initialValues,
19035d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            int tableId, long rowId) {
1904b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        String path = initialValues.getAsString(MediaStore.MediaColumns.DATA);
1905e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // path might be null for playlists created on the device
19065d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
1907b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        ContentValues values = new ContentValues();
19081717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood        values.put(ObjectColumns.MEDIA_TABLE, tableId);
19091717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood        values.put(ObjectColumns.MEDIA_ID, rowId);
1910b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
19115d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        if (objectHandle == 0) {
1912e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (path != null) {
1913e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                values.put(ObjectColumns.DATA, path);
1914e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
1915b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
19165d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            Long size = initialValues.getAsLong(MediaStore.MediaColumns.SIZE);
19175d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (size == null) {
1918e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
1919e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    File file = new File(path);
1920e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    values.put(ObjectColumns.SIZE, file.length());
1921e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
19225d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            } else {
19235d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                values.put(ObjectColumns.SIZE, size);
19245d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
1925b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
19265d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            Long parent = initialValues.getAsLong(ObjectColumns.PARENT);
19275d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (parent == null) {
1928e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
1929e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    long parentId = getParent(db, path);
1930e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    values.put(ObjectColumns.PARENT, parentId);
1931e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
19325d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            } else {
19335d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                values.put(ObjectColumns.PARENT, parent);
19345d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
1935b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
19365d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            Integer format = initialValues.getAsInteger(ObjectColumns.FORMAT);
19375d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (format == null) {
1938e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path == null) {
1939e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // special case device created playlists
1940e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    if (tableId == AUDIO_PLAYLISTS) {
1941e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        values.put(ObjectColumns.FORMAT, Mtp.Object.FORMAT_ABSTRACT_AV_PLAYLIST);
1942e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // create a file path for the benefit of MTP
1943e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        path = Environment.getExternalStorageDirectory().getAbsolutePath()
1944e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                                + "/Playlists/" + initialValues.getAsString(Audio.Playlists.NAME);
1945e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        values.put(MediaStore.MediaColumns.DATA, path);
1946e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        values.put(ObjectColumns.PARENT, getParent(db, path));
1947e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    } else {
1948e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        Log.e(TAG, "path is null in insertObject()");
1949e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
1950e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                } else {
1951e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    String mimeType = initialValues.getAsString(MediaStore.MediaColumns.MIME_TYPE);
1952e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    values.put(ObjectColumns.FORMAT, MediaFile.getFormatCode(path, mimeType));
1953e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
19545d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            } else {
19555d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                values.put(ObjectColumns.FORMAT, format);
19565d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
1957b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
19585d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            Integer modified = initialValues.getAsInteger(MediaStore.MediaColumns.DATE_MODIFIED);
19595d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (modified != null) {
19605d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                values.put(ObjectColumns.DATE_MODIFIED, modified);
19615d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
19625d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
19635d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            objectHandle = db.insert("objects", ObjectColumns.DATE_MODIFIED, values);
1964d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (LOCAL_LOGV) Log.v(TAG, "insertObject: values=" + values + " returned: " + objectHandle);
19655d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
19665d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            // only need to update MEDIA_TABLE and MEDIA_ID
19675d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            db.update("objects", values, ObjectColumns._ID + "=?",
19685d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                    new String[] { Long.toString(objectHandle) });
19695d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        }
1970d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        return objectHandle;
19711717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    }
19721717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
19735d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood    private int deleteObject(SQLiteDatabase db, String volume, String table,
19745d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            String where, String[] whereArgs) {
1975e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // delete from corresponding media table as well
1976e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        Cursor c = db.query("objects", mMediaTableColumns, where, whereArgs, null, null, null);
1977e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
19785d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (c != null && c.moveToNext()) {
19795d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                int mediaTable = c.getInt(1);
19805d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                long mediaId = c.getLong(2);
19815d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                if (mediaId > 0) {
19825d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                    // call back to our delete method rather than deleting directly
19835d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                    // so notifications will be sent.
19845d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                    switch (mediaTable) {
19855d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                        case IMAGES_MEDIA:
19865d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            delete(ContentUris.withAppendedId(
19871717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                                    Images.Media.getContentUri(volume), mediaId), null, null);
19885d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            break;
19895d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                        case AUDIO_MEDIA:
19905d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            delete(ContentUris.withAppendedId(
19911717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                                    Audio.Media.getContentUri(volume), mediaId), null, null);
19925d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            break;
19935d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                        case VIDEO_MEDIA:
19945d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            delete(ContentUris.withAppendedId(
19951717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                                    Video.Media.getContentUri(volume), mediaId), null, null);
19965d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            break;
19975d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                        case AUDIO_PLAYLISTS:
19985d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            delete(ContentUris.withAppendedId(
19991717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                                    Audio.Playlists.getContentUri(volume), mediaId), null, null);
20005d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            break;
20015d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                        default:
20025d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            Log.e(TAG, "unknown mediaTable " + mediaTable + " in deleteObject()");
20035d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                            break;
20045d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                    }
20051717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                }
20061717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood            }
20075d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } finally {
20085d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (c != null) {
20095d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                c.close();
20105d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
20111717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood        }
20121717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood        return db.delete("objects", where, whereArgs);
2013b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
2014b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2015e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private Cursor getObjectReferences(SQLiteDatabase db, int handle) {
2016e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood       Cursor c = db.query("objects", mMediaTableColumns, "_id=?",
2017e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
2018e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
2019e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
2020e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
2021e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int mediaTable = c.getInt(1);
2022e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                long mediaId = c.getLong(2);
2023e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (mediaTable != AUDIO_PLAYLISTS) {
2024e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
2025e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return null;
2026e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2027e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return db.rawQuery(OBJECT_REFERENCES_QUERY,
2028e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        new String[] { Long.toString(mediaId) } );
2029e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2030e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
2031e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
2032e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
2033e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2034e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2035e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return null;
2036e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
2037e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2038e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private int setObjectReferences(SQLiteDatabase db, int handle, ContentValues values[]) {
2039e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // first look up the media table and media ID for the object
2040e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        long playlistId = 0;
2041e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        Cursor c = db.query("objects", mMediaTableColumns, "_id=?",
2042e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
2043e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
2044e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
2045e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
2046e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int mediaTable = c.getInt(1);
2047e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (mediaTable != AUDIO_PLAYLISTS) {
2048e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
2049e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return 0;
2050e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2051e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                playlistId = c.getLong(2);
2052e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2053e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
2054e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
2055e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
2056e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2057e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2058e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (playlistId == 0) {
2059e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return 0;
2060e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2061e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2062e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // next delete any existing entries
2063e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood       db.delete("audio_playlists_map", "playlist_id=?",
2064e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] { Long.toString(playlistId) });
2065e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2066e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // finally add the new entries
2067e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int count = values.length;
2068e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int added = 0;
2069e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
2070e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        for (int i = 0; i < count; i++) {
2071e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // convert object ID to audio ID
2072e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long audioId = 0;
2073e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID);
2074e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            c = db.query("objects", mMediaTableColumns, "_id=?",
2075e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    new String[] {  Long.toString(objectId) },
2076e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    null, null, null);
2077e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            try {
2078e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null && c.moveToNext()) {
2079e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    int mediaTable = c.getInt(1);
2080e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    if (mediaTable != AUDIO_MEDIA) {
2081e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // we only allow audio files in playlists, so skip
2082e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        continue;
2083e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
2084e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    audioId = c.getLong(2);
2085e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2086e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            } finally {
2087e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null) {
2088e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    c.close();
2089e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2090e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2091e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (audioId != 0) {
2092e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                ContentValues v = new ContentValues();
2093e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
2094e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
2095e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added++);
2096e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList[i] = v;
2097e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2098e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2099e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (added < count) {
2100e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // we weren't able to find everything on the list, so lets resize the array
2101e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // and pass what we have.
2102e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            ContentValues[] newValues = new ContentValues[added];
2103e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            System.arraycopy(valuesList, 0, newValues, 0, added);
2104e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            valuesList = newValues;
2105e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2106e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return playlistBulkInsert(db,
2107e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId),
2108e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList);
2109e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
2110e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
21115d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood    private Uri insertInternal(Uri uri, int match, ContentValues initialValues) {
2112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
21135d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int objectHandle = 0;
2114d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        String mediaTable = null;
2115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2116d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
2117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
2118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
2119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
2120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
2121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
2124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
2125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null && match != VOLUMES) {
2126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = (match == VOLUMES ? null : database.getWritableDatabase());
2130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (initialValues == null) {
2132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            initialValues = new ContentValues();
21335d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
2134d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Integer i = initialValues.getAsInteger(
2135d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
21365d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (i != null) {
21375d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                objectHandle = i.intValue();
2138d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                initialValues.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
21395d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
2140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
2143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
2144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg", "DCIM/Camera");
2145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String data = values.getAsString(MediaColumns.DATA);
2148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
2149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    computeDisplayName(data, values);
2150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeBucketValues(data, values);
2152b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                computeTakenTime(values);
2153d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                mediaTable = "images";
2154d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                rowId = db.insert(mediaTable, "name", values);
2155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
2158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
21599299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    requestMediaThumbnail(data, newUri, MediaThumbRequest.PRIORITY_NORMAL, 0);
2160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2164b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
2165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
2166b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg",
2167b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
2168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
2169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
2171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContentUri(uri.getPathSegments().get(0)), rowId);
2172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2176b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
2177b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
2178b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg",
2179b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
2180b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
2181b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
2182b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
2183b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            getContentUri(uri.getPathSegments().get(0)), rowId);
2184b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2185b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2186b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
2187b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
2189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // SQLite Views are read-only, so we need to deconstruct this
2190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // insert and do inserts into the underlying tables.
2191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // If doing this here turns out to be a performance bottleneck,
2192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // consider moving this to native code and using triggers on
2193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // the view.
2194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
2195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2196acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                // TODO Remove this and actually store the album_artist in the
2197acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                // database. For now this is here so the media scanner can start
2198acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                // sending us the album_artist, even though it's not in the db yet.
2199acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                values.remove(MediaStore.Audio.Media.ALBUM_ARTIST);
2200acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
2201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Insert the artist into the artist table and remove it from
2202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // the input values
2203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Object so = values.get("artist");
2204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String s = (so == null ? "" : so.toString());
2205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.remove("artist");
2206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long artistRowId;
2207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashMap<String, Long> artistCache = database.mArtistCache;
220859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                String path = values.getAsString("_data");
2209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                synchronized(artistCache) {
2210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Long temp = artistCache.get(s);
2211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (temp == null) {
2212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist",
2213a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                s, s, path, 0, null, artistCache, uri);
2214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    } else {
2215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        artistRowId = temp.longValue();
2216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2218a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                String artist = s;
2219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Do the same for the album field
2221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                so = values.get("album");
2222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                s = (so == null ? "" : so.toString());
2223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.remove("album");
2224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long albumRowId;
2225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashMap<String, Long> albumCache = database.mAlbumCache;
2226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                synchronized(albumCache) {
222759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    int albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
222859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    String cacheName = s + albumhash;
222959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    Long temp = albumCache.get(cacheName);
2230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (temp == null) {
2231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        albumRowId = getKeyIdForName(db, "albums", "album_key", "album",
2232a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                s, cacheName, path, albumhash, artist, albumCache, uri);
2233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    } else {
2234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        albumRowId = temp;
2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("artist_id", Integer.toString((int)artistRowId));
2239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("album_id", Integer.toString((int)albumRowId));
2240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                so = values.getAsString("title");
2241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                s = (so == null ? "" : so.toString());
2242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("title_key", MediaStore.Audio.keyFor(s));
2243358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                // do a final trim of the title, in case it started with the special
2244358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                // "sort first" character (ascii \001)
2245358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                values.remove("title");
2246358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                values.put("title", s.trim());
2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeDisplayName(values.getAsString("_data"), values);
2249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2251d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                mediaTable = "audio_meta";
2252d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                rowId = db.insert(mediaTable, "duration", values);
2253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
2255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
2260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
2261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
2262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
2263ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
2264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
2266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
2271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
2272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
2273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
2274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
2275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
2276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
2278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
2283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres", "audio_id", initialValues);
2284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
2286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
2291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
2292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
2293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
2294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
2295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
2297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
2302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
2303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
2304d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                mediaTable = "audio_playlists";
2305d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                rowId = db.insert(mediaTable, "name", initialValues);
2306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
2308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
2314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
2315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
2316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
2317ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
2318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
2320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
2325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = ensureFile(database.mInternal, initialValues, ".3gp", "video");
2326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String data = values.getAsString("_data");
2327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeDisplayName(data, values);
2328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeBucketValues(data, values);
2329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2330498b62c2912302a23532c73a028a7684c5df33caRay Chen                computeTakenTime(values);
2331d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                mediaTable = "video";
2332d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                rowId = db.insert(mediaTable, "artist", values);
2333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2334b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
2335b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            uri.getPathSegments().get(0)), rowId);
23369299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    requestMediaThumbnail(data, newUri, MediaThumbRequest.PRIORITY_NORMAL, 0);
2337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART:
2342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database.mInternal) {
2343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
2344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = null;
2346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
2347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
2348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
2349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
2350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    values = initialValues;
2351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("album_art", "_data", values);
2353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
2359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return attachVolume(initialValues.getAsString("name"));
2360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
23615d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            case MTP_OBJECTS: {
2362abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood                try {
2363abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood                    mDisableMtpObjectCallbacks = true;
2364d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    rowId = db.insert("objects", ObjectColumns.DATE_MODIFIED, initialValues);
2365abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood                    if (rowId > 0) {
2366abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood                        newUri = MtpObjects.getContentUri(uri.getPathSegments().get(0), rowId);
2367abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood                    }
2368abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood                } finally {
2369abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood                    mDisableMtpObjectCallbacks = false;
23705d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                }
23715d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                break;
23725d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
23735d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
2374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
2376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2378abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood        // don't bother populating internal objects database since we are not sharing it via MTP
2379abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood        if (rowId > 0 && mediaTable != null && !database.mInternal) {
2380d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            long objectId = insertObject(db, objectHandle, initialValues, match, rowId);
2381d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2382d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // Set object_id in the media table
2383d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            ContentValues values = new ContentValues();
2384d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            values.put(MediaColumns.MTP_OBJECT_ID, objectId);
2385d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            db.update(mediaTable, values, MediaColumns._ID + "=?",
2386d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    new String[] { Long.toString(rowId) });
2387d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2388d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            sendObjectAdded(objectId);
2389b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
2390b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
2392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2394cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
2395cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2396cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
2397cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
2398cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
2399cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
2400cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
2401cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
2402cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
2403cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
2404cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
2405cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
2406cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
2407cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
2408cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
2409cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
2410cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
2411cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
2412cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
2413cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
2414cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
2415cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
2416cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
2417cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
2418cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
2419cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
2420cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
2421cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
2422cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
2423cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
2424cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
2425cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
2426cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
2427cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
2428cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
2429cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
24309299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
2431b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
2432e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
2433e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
2434e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
24359299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
2436e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
2437e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
2438e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
2439e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
2440e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
2441e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
2442e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
2443b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
2444b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2445b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
2446b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
2448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
2450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
2451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
2453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
2454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
2455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
2456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return Environment.getExternalStorageDirectory()
2458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                + "/" + directoryName + "/" + name + preferredExtension;
2459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
2463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
2464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
2465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
2466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
2468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
2469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
2470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
2471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
2472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
2473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
2474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
2475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.getParentFile().mkdirs();
2476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
2477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return file.createNewFile();
2478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
2479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
2480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
2482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
2486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
2487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
2488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
2491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
2492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
2494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
2495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
2496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
24979f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
24989f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                out.table = "images";
24999f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
25009f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
2501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "images";
2503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
2504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2506b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
2507b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
2508b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
2509b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
2510b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2511b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio";
2514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio";
2518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
2519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
2523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
2524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
2528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
2529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
2530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
2531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
2534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
2535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
2539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
2540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
2541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
2545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
2549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
2553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
2554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
2555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
2558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
2559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3) +
2560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND audio_id =" + uri.getPathSegments().get(5);
2561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
2565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
2569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
2570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
2574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
2575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
2579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
2580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
2581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
2584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
2585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
2586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "video";
2590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "video";
2594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
2595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2597b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
2598b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
2599b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
2600b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
2601b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2602b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
26031717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood            case MTP_OBJECTS_ID:
26041717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                where = "_id=" + uri.getPathSegments().get(2);
26051717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood            case MTP_OBJECTS:
26061717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                out.table = "objects";
26071717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                break;
26081717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
2609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
2611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
2612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
2615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
2616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
2617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
2618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
2620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
2623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
2628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
2632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
2633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
2634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
2635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
2637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
2638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match != VOLUMES_ID) {
2641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
2642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
2643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
2644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown URI: " + uri);
2645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
2647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
2649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
2651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    case AUDIO_MEDIA:
2652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    case AUDIO_MEDIA_ID:
2653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete("audio_meta",
2654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
26561717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                    case MTP_OBJECTS:
26571717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                        throw new UnsupportedOperationException(
26581717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                                "Deleting multiple objects via MTP not supported");
26591717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                    case MTP_OBJECTS_ID:
2660d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        try {
2661d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // don't send objectRemoved event since this originated from MTP
2662d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = true;
2663d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // return here to avoid calling notifyChange()
2664d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            return deleteObject(db, uri.getPathSegments().get(0),
2665d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                                    sGetTableAndWhereParam.table,
2666d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                                    sGetTableAndWhereParam.where, whereArgs);
2667d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        } finally {
2668d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = false;
2669d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        }
2670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
2671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
2672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
2673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
2674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getContext().getContentResolver().notifyChange(uri, null);
2676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            detachVolume(uri);
2679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            count = 1;
2680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
2683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
2687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
2688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
2689b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
2690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
2692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getWritableDatabase();
2697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
2699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
2700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
2702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
2703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
2704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
2705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
2706acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        // TODO Remove this and actually store the album_artist in the
2707acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        // database. For now this is here so the media scanner can start
2708acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        // sending us the album_artist, even though it's not in the db yet.
2709acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        values.remove(MediaStore.Audio.Media.ALBUM_ARTIST);
271007656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen
2711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
2712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
2713a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
27146006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("artist");
2715a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            HashMap<String, Long> artistCache = database.mArtistCache;
2718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
2719a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
2720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
2721acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                    artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist",
2722acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
2727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
2728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
273059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
2731a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
27326006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("album");
2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
273459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            String path = values.getAsString("_data");
273559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
273659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            if (path == null) {
273759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                // If the path is null, we don't have a hash for the file in question.
273859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Log.w(TAG, "Update without specified path.");
273959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
274059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
274159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
2742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
2743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
2744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            HashMap<String, Long> albumCache = database.mAlbumCache;
2745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
274659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
274759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = getKeyIdForName(db, "albums", "album_key", "album",
2750a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
2751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
2752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
2753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
2754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
2755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
2756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
2759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
2760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
2761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
2762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
2763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
2764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
2765e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
2766e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
2767e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
2768e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
2769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update("audio_meta", values, sGetTableAndWhereParam.where,
2772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                whereArgs);
2773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
2776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
2777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
2778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
2779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
2780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
2781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
2782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
2783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
2784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
2785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
2786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
2787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
2788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
2789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
2790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2791b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                        computeTakenTime(values);
2792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
2793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
279401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
279501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
279601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
279701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
279801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
279901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
280001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
2801b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
2802216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                try {
2803216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    while (c.moveToNext()) {
2804216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        long magic = c.getLong(2);
2805216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        if (magic == 0) {
2806216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                            requestMediaThumbnail(c.getString(1), uri,
2807216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                                    MediaThumbRequest.PRIORITY_NORMAL, 0);
2808216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        }
2809b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
2810216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                } finally {
2811216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    c.close();
2812b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
2813b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
2814b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
2815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2817f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
2818f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2819f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
2820f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
2821f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
2822f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
2823f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
2824f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
2825f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
2826f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
2827f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            return movePlaylistEntry(db, playlist, oldpos, newpos);
2828f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
2829f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
2830f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
2831f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
2832f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
2833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
2834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
2835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
2836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2839cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
2840cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
2841cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
2842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
2845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2847f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    private int movePlaylistEntry(SQLiteDatabase db, long playlist, int from, int to) {
2848f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
2849f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
2850f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
2851f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
2852f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
2853f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            int numlines = 0;
2854f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
2855f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=" + from +
2856f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
2857f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
2858f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
2859f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
2860f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
2861f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
2862f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order<=" + to + " AND play_order>" + from +
2863f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
2864f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
2865f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
2866f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
2867f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order>=" + to + " AND play_order<" + from +
2868f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
2869f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
2870f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
2871f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to +
2872f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
2873f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
2874f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
2875f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
2876f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            getContext().getContentResolver().notifyChange(uri, null);
2877f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return numlines;
2878f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
2879f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
2880f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
2881f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
2882f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
2883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
2884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
2885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
2886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
2889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
289071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
2891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
289271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
289371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
289471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
289571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
289671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
289771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
289871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
289971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
290071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
290171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
290271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
290371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
290471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
290571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
290671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
290771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
290871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
290971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
291071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
291171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
291271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
291371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
291471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
291571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
291671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
291771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = openFile(newUri, mode);  // recursive call
291871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
291971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
292071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, null);
292171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
292271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
292371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
292471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
292571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
292671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
2927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd = openFileHelper(uri, mode);
2929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
293071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
293171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
293271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
293371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
293471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
2935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
2936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
2937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
2938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
2939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
2940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
2942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
2943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
294471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
2945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
2946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
2947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
2948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
2949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        null, null, null, null, null);
295071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
2951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
295271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, uri);
2953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
2955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
295671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
295771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
295871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
2959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
2961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
2964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
2965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
2966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
2967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
2968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2970a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private void makeThumbAsync(SQLiteDatabase db, String path, long album_id) {
29718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
29728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
29738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
29748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
29758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
29768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
29778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
29788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
29798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
29808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
2981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
2982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
2983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
2984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
2985a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
29868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
29878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
29888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
29898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
29908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
29918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
29928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
29938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
29948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
29958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
29968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
2997b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
2998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
2999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
30018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
30028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
30038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
30048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
3005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
3007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
3008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
3009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
3010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
30118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
30128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
3013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
3014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3015d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
30163f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
30173f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
3018d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
3019d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
3020d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
3021d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
3022d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
3023d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
30248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
3025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
3026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
3027d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
30283f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
30293f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String sdroot = Environment.getExternalStorageDirectory().getAbsolutePath();
30300fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
30312f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
3032d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
3033d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
3034d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
3035d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
3036d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
3037ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                        } else if (!artPath.equalsIgnoreCase(sdroot) &&
3038ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                                !artPath.equalsIgnoreCase(dwndir)) {
3039d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
3040d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
3041d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
3042d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
3043d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
3044d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
3045d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
3046d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
3047d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
3048d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
3049d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3050d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
3051d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
3052d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
3053d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
3054d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3055d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
3056d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
3057d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
3058d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
3059d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3060d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
3061d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
3062d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3063d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
3064d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
3065d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3066d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
3067d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
3068d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
3069d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
3070d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
3071d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
3072d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
3073d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
3074d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
30753f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
3076d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
3077d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            compressed = new byte[(int)file.length()];
3078d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
3079d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
3080d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
3081d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
3082d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
3083d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
3084d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
3085d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
3086d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
3087d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
3088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
3089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
30938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
30948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
3095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
30968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
30978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
3098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
30998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
31008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    Uri getAlbumArtOutputUri(SQLiteDatabase db, long album_id, Uri albumart_uri) {
31018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
31028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
31038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
31048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
31058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Cursor c = query(albumart_uri, new String [] { "_data" },
31068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
310771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
31088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                String albumart_path = c.getString(0);
31098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (ensureFileExists(albumart_path)) {
31108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = albumart_uri;
3111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
311271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } else {
311371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                albumart_uri = null;
3114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
31158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            c.close();
311671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
311771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
31188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
31198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
31208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
31218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
31228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                long rowId = db.insert("album_art", "_data", values);
31238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
31248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
3125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
31268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
31278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
31288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
31298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
31308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
31318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
31328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
31338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
31348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
31358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
31368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) {
31378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean success = false;
31388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
31398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            OutputStream outstream = getContext().getContentResolver().openOutputStream(out);
31408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
31418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
31428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
31438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
31448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
31458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = true;
31468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
31478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = bm.compress(Bitmap.CompressFormat.JPEG, 75, outstream);
3148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
31498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
31508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            outstream.close();
31518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (FileNotFoundException ex) {
31528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
3153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (IOException ex) {
31548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
31558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
31568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (!success) {
31578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // the thumbnail was not written successfully, delete the entry that refers to it
31588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            getContext().getContentResolver().delete(out, null, null);
3159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
31608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
31618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
316271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor getThumb(SQLiteDatabase db, String path, long album_id,
316371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Uri albumart_uri) {
316471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
316571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
316671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
316771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
316871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
316971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
317071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
317171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
317271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
31738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
3174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
31758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
317671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
31778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
31788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
31798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
31808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
31818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
31828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
31838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
31848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
31858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
31868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
31878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
31888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
31898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
31908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // TODO: don't hardcode the size
31918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            while (opts.outHeight > 320 || opts.outWidth > 320) {
31928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
31938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
31948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
31958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
31968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
31978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
31988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
31998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
32008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
32018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
32028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
32038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
32048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
32058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
32068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
32078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
3208a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
3209a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
3210a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
3211a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
3212a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
32138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
32148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
32158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
32168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
32178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
32188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
321971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
32208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
32218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
322271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
322371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
322471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
322571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
322624001394f571b1f0378840cbf299288e4df10508Bjorn Bringert                return ParcelFileDescriptor.fromData(compressed, "albumthumb");
322771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
322871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
322971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
3230a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
3231a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
3232a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
3233a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
3234a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
3235a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
3236a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                Uri out = getAlbumArtOutputUri(d.db, d.album_id, d.albumart_uri);
3237a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
3238a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
3239a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
3240a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
3241a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    ParcelFileDescriptor pfd = openFileHelper(out, "r");
3242a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
3243a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
3244a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
3245a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (FileNotFoundException ex) {
3246a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
3247a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
3248a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
3249a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
3250a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
3251a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
3252a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
325371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
325471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
32558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
325671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
3257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
3260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
3261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
3262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
3263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
3264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
3265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
3266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
326759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
326859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
326959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
3270a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
3271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
3272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
3273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
3274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
3275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
3276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private long getKeyIdForName(SQLiteDatabase db, String table, String keyField, String nameField,
327759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
3278a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
3279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
3280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
3282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
3283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
3285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
3287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
3288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
329059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
3291e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
329259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
329359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // To distinguish same-named albums, we append a hash of the path.
329459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
329559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
329659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
329759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
329859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
3299a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
330059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
3301a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
3302a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
3303a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
330459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
330559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
3306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
3307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
3308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
3310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
3311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
3312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
3313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
3314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
3315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
3316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
331759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
3318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
3319a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen                            makeThumbAsync(db, path, rowId);
3320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
3322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
3323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
3324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
3325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
3329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
3330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
3331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
3332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
3334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
3335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
3336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
3337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
3338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
3339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
3340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
3341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
3342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
3343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
3344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
3345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
3349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
3350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
3351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
3352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
3355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
3356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
335859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
335959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
3360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
3362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
3365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
3366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
3367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
3368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
3369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
3370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
3371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
3372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
3373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
3374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
3375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
3376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
3377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
3378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
3379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
3380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
3381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
3382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
3384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
3385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
3386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
3388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
3390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
3392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
3396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
3397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
3398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
3399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
3401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
3404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
3405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
3409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
3410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
3411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
3412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
3413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
3414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
3415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
3416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (uri.getPathSegments().size() > 1) {
3417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
3418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
3421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
3424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
3425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
3426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
3427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
3428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
3429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
3430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
3431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
3432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {
3433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
3434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
3435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
3438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
3439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
3440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper db;
3443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
3444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db = new DatabaseHelper(getContext(), INTERNAL_DATABASE_NAME, true);
3445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
3446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String path = Environment.getExternalStorageDirectory().getPath();
3447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int volumeID = FileUtils.getFatVolumeId(path);
3448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
3449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // generate database name based on volume ID
3451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
3452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db = new DatabaseHelper(getContext(), dbName, false);
34530027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                mVolumeId = volumeID;
3454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
3456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.put(volume, db);
3459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!db.mInternal) {
3461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
3462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File[] files = new File(
3463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        Environment.getExternalStorageDirectory(),
3464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ALBUM_THUMB_FOLDER).listFiles();
3465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
3466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
3467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
3468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
3471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
3472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
3473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
3474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
3475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
3477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
3478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
3481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
3482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
3483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
3484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
3490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
3491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
3494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
3495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
3496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
3497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
3498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
3499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
3500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
3501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {
3502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
3503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
3504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
3507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
3508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
3510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
3511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
3512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
3513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
3516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
3517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
3518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
3520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
3521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
3522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
3523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch (SQLException e) {
3524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
3525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
3528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
3529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
3532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
3533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
3536ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer    private static final boolean LOCAL_LOGV = false;
3537bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin    private static final int DATABASE_VERSION = 96;
3538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
3539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
3541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
3542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
3544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
3545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
3546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
3548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
3550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
3552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
3553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
35540027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
35550027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private int mVolumeId;
35560027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
3557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
3558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
3559268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
3560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
3562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
3563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
35641717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
35651717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // are stored in the "objects" table, so do not renumber them unless you also add
35661717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // a corresponding database upgrade step for it.
3567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
3568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
3569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
3570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
3571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
3573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
3574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
3575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
3576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
3577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
3578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
3579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
3580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
3581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS_ID = 109;
3582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
3583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
3584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
3585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
3586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
3587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
3588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
3589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
3590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
3591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
3592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
359371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
3594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
3596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
3597b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
3598b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
3599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
3601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
3602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3603a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
3604a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
3605a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
3606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
3608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
36090027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
36100027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
3611b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    private static final int MTP_OBJECTS = 700;
3612b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    private static final int MTP_OBJECTS_ID = 701;
3613e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final int MTP_OBJECT_REFERENCES = 702;
3614b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
3616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
3617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3618b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
3619b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
3620b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
3621b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
3623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
3624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
3625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
3626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3627b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
3628b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
3629b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
3630b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
3631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
3632b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] EXTERNAL_DATABASE_TABLES = new String[] {
3634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "images",
3635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "thumbnails",
3636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_meta",
3637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "artists",
3638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "albums",
3639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_genres",
3640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_genres_map",
3641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_playlists",
3642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_playlists_map",
3643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "video",
3644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
3645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3646e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final String OBJECT_REFERENCES_QUERY =
3647e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        "SELECT objects._id FROM objects, audio_playlists_map"
3648e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        + " WHERE audio_playlists_map." + Audio.Playlists.Members.PLAYLIST_ID + "=?"
3649e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        + " AND audio_playlists_map.audio_id = objects." + ObjectColumns.MEDIA_ID
3650e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        + " AND objects." + ObjectColumns.MEDIA_TABLE + "=" + AUDIO_MEDIA
3651e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        + " ORDER BY audio_playlists_map." + Audio.Playlists.Members.PLAY_ORDER;
3652e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
3654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
3655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
3656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
3657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
3658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
3659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
3661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
3662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
3663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
3664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
3665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
3666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
3667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
3668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
3669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members/#", AUDIO_GENRES_ID_MEMBERS_ID);
3670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
3671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
3672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
3673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
3674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
3675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
3676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
3677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
3678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
3679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
3680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
368171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
3682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
3684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
3685b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
3686b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
3687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
3689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
36900027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
36910027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
3692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
3693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
3694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3695b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        // Used by MTP implementation
3696b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS);
3697b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID);
3698e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES);
3699b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3700a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
3701a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
3702a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
3703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
3704a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
3705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
3706a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
3707a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
3708a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
3709a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
3710a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
3711a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
3712a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
3713a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
3714a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
3715a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
3716a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
3717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
3719