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