MediaProvider.java revision 9311c7ff9d35ca3acc908da3da7a79fbf7a8da6b
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;
3590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissenimport android.content.pm.PackageManager.NameNotFoundException;
3670676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources;
37702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
38ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
390027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
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;
4738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection;
4838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection.MediaScannerConnectionClient;
49b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
5090345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants;
519be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwoodimport android.mtp.MtpStorage;
52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
5438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.os.Bundle;
55702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
56702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils;
57702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
58ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread;
59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
60702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
61702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
62d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException;
6310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.os.SystemClock;
644f2186758ee1c6eaa702bf1511b233b26143b631Mike Lockwoodimport android.os.SystemProperties;
65c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageManager;
661f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwoodimport android.os.storage.StorageVolume;
67ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager;
68702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
69702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
70702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
715bf5c26a3499d584332074baab97392696ed9614Ray Chenimport android.provider.MediaStore.Files;
7270676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns;
73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
7470676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns;
75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
76702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
77702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
7810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils;
79702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
80702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
81702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
8210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor;
83702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
84702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
85702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
8710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter;
88702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.text.Collator;
89cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
9038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Arrays;
9110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection;
92702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
93702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
94702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
95f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
9638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Locale;
97b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
99702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
100166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.ErrnoException;
101166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.Libcore;
102166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
112b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
113b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
116d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1187f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    // In memory cache of path<->id mappings, to speed up inserts during media scan
1197f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
1207f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
1218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
1228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
1238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
1248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
1258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
1268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
12720434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
12820434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
129b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
130b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
131b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
132b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
133f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood    private boolean mCaseInsensitivePaths;
1349be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private static String[] mExternalStoragePaths;
13517ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood
136a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
137a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
138a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
139a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
140a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
141a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
142a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
143a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
144a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
145a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
146a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
147a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
148a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
149a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
150a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
151a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
152a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
153a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
154ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
155ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
156a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
157a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
158a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
159a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
160a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
161a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
16663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
169a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
170a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
173a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
174a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
17663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
17763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
178e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
17963f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
180a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
181a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
18263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
18363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
184a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
185a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTableColumns = new String[] {
18616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            FileColumns._ID,
187afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            FileColumns.MEDIA_TYPE,
1881717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    };
1891717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
1907f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    private static final String[] sIdOnlyColumn = new String[] {
1917f36494e085c26c69cd5925e54028822025eff29Marco Nelissen        FileColumns._ID
1927f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    };
1937f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
194166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    private static final String[] sDataOnlyColumn = new String[] {
195166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        FileColumns.DATA
196166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    };
197166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
198a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTypeDataId = new String[] {
199a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.MEDIA_TYPE,
200a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.DATA,
201a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns._ID
202a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen};
203a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
204a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
205a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
2101f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                StorageVolume storage = (StorageVolume)intent.getParcelableExtra(
2111f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        StorageVolume.EXTRA_STORAGE_VOLUME);
2121f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // If primary external storage is ejected, then remove the external volume
2131f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // notify all cursors backed by data on that volume.
2141f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                if (storage.getPath().equals(mExternalStoragePaths[0])) {
2151f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    detachVolume(Uri.parse("content://media/external"));
2161f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    sFolderArtMap.clear();
2171f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    MiniThumbFile.reset();
2181f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                } else {
2191f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // If secondary external storage is ejected, then we delete all database
2201f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // entries for that storage from the files table.
2211f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    synchronized (mDatabases) {
2221f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        DatabaseHelper database = mDatabases.get(EXTERNAL_VOLUME);
2231f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        Uri uri = Uri.parse("file://" + storage.getPath());
2241f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        if (database != null) {
2251f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            try {
2261f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // Send media scanner started and stopped broadcasts for apps that rely
2271f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // on these Intents for coarse grained media database notifications.
2281f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2291f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
2301f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood
2311f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // don't send objectRemoved events - MTP be sending StorageRemoved anyway
2321f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = true;
2331f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.d(TAG, "deleting all entries for storage " + storage);
2341f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                SQLiteDatabase db = database.getWritableDatabase();
2354b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // First clear the file path to disable the _DELETE_FILE database hook.
2364b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // We do this to avoid deleting files if the volume is remounted while
2374b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // we are still processing the unmount event.
2384b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                ContentValues values = new ContentValues();
2394b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                values.put(Files.FileColumns.DATA, "");
2404b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String where = FileColumns.STORAGE_ID + "=?";
2414b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
2424b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.update("files", values, where, whereArgs);
2434b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // now delete the records
2444b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.delete("files", where, whereArgs);
2454b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // notify on media Uris as well as the files Uri
2464b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2474b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Audio.Media.getContentUri(EXTERNAL_VOLUME), null);
2484b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2494b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Images.Media.getContentUri(EXTERNAL_VOLUME), null);
2504b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2514b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Video.Media.getContentUri(EXTERNAL_VOLUME), null);
2521f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.getContentResolver().notifyChange(
2531f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        Files.getContentUri(EXTERNAL_VOLUME), null);
2541f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } catch (Exception e) {
2551f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.e(TAG, "exception deleting storage entries", e);
2561f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } finally {
2571f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2581f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
2591f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = false;
2601f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            }
2611f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        }
2621f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    }
2631f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                }
264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
268d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    // set to disable sending events when the operation originates from MTP
269d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private boolean mDisableMtpObjectCallbacks;
270d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
271d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final SQLiteDatabase.CustomFunction mObjectRemovedCallback =
272d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                new SQLiteDatabase.CustomFunction() {
273d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void callback(String[] args) {
2747f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // We could remove only the deleted entry from the cache, but that
2757f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // requires the path, which we don't have here, so instead we just
2767f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // clear the entire cache.
2777f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: include the path in the callback and only remove the affected
2787f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // entry from the cache
2797f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            mDirectoryCache.clear();
280d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // do nothing if the operation originated from MTP
281d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mDisableMtpObjectCallbacks) return;
282d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
283d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "object removed " + args[0]);
284d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            IMtpService mtpService = mMtpService;
285d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mtpService != null) {
286d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                try {
287d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectRemoved(Integer.parseInt(args[0]));
288d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                } catch (NumberFormatException e) {
289d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e);
290d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
291d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
292d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
293d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
294d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
300fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static final class DatabaseHelper extends SQLiteOpenHelper {
301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
3025524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        final String mName;
303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
304fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final boolean mEarlyUpgrade;
305fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final SQLiteDatabase.CustomFunction mObjectRemovedCallback;
3065524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        boolean mUpgradeAttempted; // Used for upgrade error handling
30710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumQueries;
30810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumUpdates;
30910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumInserts;
31010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumDeletes;
31110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStartTime;
31210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStopTime;
313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
318fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        public DatabaseHelper(Context context, String name, boolean internal,
319fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                boolean earlyUpgrade,
320fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                SQLiteDatabase.CustomFunction objectRemovedCallback) {
32190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            super(context, name, null, getDatabaseVersion(context));
322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
3235524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mName = name;
324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
325fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mEarlyUpgrade = earlyUpgrade;
326fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mObjectRemovedCallback = objectRemovedCallback;
327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
33490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, 0, getDatabaseVersion(mContext));
335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
3435524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = true;
34490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, oldV, newV);
345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
347db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        @Override
348db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        public synchronized SQLiteDatabase getWritableDatabase() {
3495524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            SQLiteDatabase result = null;
3505524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = false;
3515524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            try {
3525524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3535524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            } catch (Exception e) {
3545524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                if (!mUpgradeAttempted) {
3555524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    Log.e(TAG, "failed to open database " + mName, e);
3565524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    return null;
3575524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                }
3585524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3595524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
3605524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // If we failed to open the database during an upgrade, delete the file and try again.
3615524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // This will result in the creation of a fresh database, which will be repopulated
3625524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // when the media scanner runs.
3635524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            if (result == null && mUpgradeAttempted) {
3645524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                mContext.getDatabasePath(mName).delete();
3655524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3665524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3675524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            return result;
3685524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        }
3695524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
371993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * For devices that have removable storage, we support keeping multiple databases
372993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * to allow users to switch between a number of cards.
373993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * On such devices, touch this particular database and garbage collect old databases.
374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
37936d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
380652c337bac290c7dfcde8725e7c771193a7f7ed6Vasu Nori            // Turn on WAL optimization
381652c337bac290c7dfcde8725e7c771193a7f7ed6Vasu Nori            db.enableWriteAheadLogging();
38236d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
385fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mEarlyUpgrade) return; // Doing early upgrade.
386fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
387fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mObjectRemovedCallback != null) {
388fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback);
389fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            }
390d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
391993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            // the code below is only needed on devices with removable storage
392993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            if (!Environment.isExternalStorageRemovable()) return;
393993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
45434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood    // synchronize on mMtpServiceConnection when accessing mMtpService
455d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private IMtpService mMtpService;
456d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
457d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
458d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood         public void onServiceConnected(ComponentName className, android.os.IBinder service) {
45934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
46034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = IMtpService.Stub.asInterface(service);
46134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
462d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
463d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
464d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void onServiceDisconnected(ComponentName className) {
46534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
46634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = null;
46734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
468d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
469d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
470d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
471ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    private static final String[] sDefaultFolderNames = {
472ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MUSIC,
473ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PODCASTS,
474ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_RINGTONES,
475ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_ALARMS,
476ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_NOTIFICATIONS,
477ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PICTURES,
478ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MOVIES,
479ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DOWNLOADS,
480ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DCIM,
481ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    };
482ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
483ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    // creates default folders (Music, Downloads, etc)
48410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) {
485ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // Use a SharedPreference to ensure we only do this once.
486ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // We don't want to annoy the user by recreating the directories
487ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // after she has deleted them.
488ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
489ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        if (prefs.getInt("created_default_folders", 0) == 0) {
490ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            for (String folderName : sDefaultFolderNames) {
491ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                File file = Environment.getExternalStoragePublicDirectory(folderName);
492ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                if (!file.exists()) {
493ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                    file.mkdirs();
49410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    insertDirectory(helper, db, file.getAbsolutePath());
495ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                }
496ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            }
497ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
498ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            SharedPreferences.Editor e = prefs.edit();
499ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.clear();
500ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.putInt("created_default_folders", 1);
501ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.commit();
502ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        }
503ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
504ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5059311c7ff9d35ca3acc908da3da7a79fbf7a8da6bMarco Nelissen    public static int getDatabaseVersion(Context context) {
50690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        try {
50790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            return context.getPackageManager().getPackageInfo(
50890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    context.getPackageName(), 0).versionCode;
50990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        } catch (NameNotFoundException e) {
51090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            throw new RuntimeException("couldn't get version code for " + context);
51190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
51290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    }
51390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen
514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
516d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        final Context context = getContext();
517d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
518acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
519acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums._ID);
520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
522acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
524acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
529acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
530acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
531acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
532acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.ALBUM_ART);
533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
53463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
53563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
536d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "%1", context.getString(R.string.artist_label));
537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
542d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.registerReceiver(mUnmountReceiver, iFilter);
543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5447d4f72d8ab71e28e9d40da87ec8c75ded254dd08Dianne Hackborn        mCaseInsensitivePaths = true;
5454f2186758ee1c6eaa702bf1511b233b26143b631Mike Lockwood
546c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        StorageManager storageManager =
547c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood                (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
548c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        mExternalStoragePaths = storageManager.getVolumePaths();
5499be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
557ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
558ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
559ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
562b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
563b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
56420434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
565b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
56620434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
567b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
568b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
569b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
57020434e032e498b716f87cce2f23dd646819218bfRay Chen                            File origFile = new File(mCurrentThumbRequest.mPath);
5714d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
57220434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.execute();
5734d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
5744d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
5754d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
57620434e032e498b716f87cce2f23dd646819218bfRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
5774d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
5784d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
579b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
5801d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
5811d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
5821d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
5831d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
5841d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
58522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                        } catch (OutOfMemoryError err) {
58622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            /*
58722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Note: Catching Errors is in most cases considered
58822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * bad practice. However, in this case it is
58922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * motivated by the fact that corrupt or very large
59022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * images may cause a huge allocation to be
59122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * requested and denied. The bitmap handling API in
59222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Android offers no other way to guard against
59322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * these problems than by catching OutOfMemoryError.
59422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             */
59522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            Log.w(TAG, err);
596b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
59720434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
59820434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
59920434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
600b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
601b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
602b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
603b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
604b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
605b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
606b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
607b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
609b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    makeThumbInternal(d);
610b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
611b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
612b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
620afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String IMAGE_COLUMNS =
621afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_size,_display_name,mime_type,title,date_added," +
622afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
623bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," +
624bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "width,height";
625bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
626bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String IMAGE_COLUMNSv407 =
627bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_size,_display_name,mime_type,title,date_added," +
628bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
629afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name";
630afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
631805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv99 =
632afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added," +
633afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
634afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
635afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark";
636afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
637805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv100 =
638805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "_data,_display_name,_size,mime_type,date_added," +
639805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
640805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
641805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "bookmark,album_artist";
642805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen
643957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang    private static final String AUDIO_COLUMNSv405 =
644957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "_data,_display_name,_size,mime_type,date_added,is_drm," +
645957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
646957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
647957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "bookmark,album_artist";
648957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
649afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String VIDEO_COLUMNS =
650afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
651afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title,duration,artist,album,resolution,description,isprivate,tags," +
652afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
653bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," +
654bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "height";
655bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
656bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String VIDEO_COLUMNSv407 =
657bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
658bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "title,duration,artist,album,resolution,description,isprivate,tags," +
659bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
660afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic,bucket_id,bucket_display_name, bookmark";
661afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
662afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified";
663afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
67290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    private static void updateDatabase(Context context, SQLiteDatabase db, boolean internal,
673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
67690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        int dbversion = getDatabaseVersion(context);
67790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (toVersion != dbversion) {
67890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + dbversion);
679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
68195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
686d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag.
687acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        // We can't downgrade from those revisions, so start over.
688022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // (the initial change to do this was wrong, so now we actually need to start over
689022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // if the database version is 84-89)
690bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair.
691bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // However version 91 was reused in a divergent development path for gingerbread,
692bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // so we need to support upgrades from 91.
693bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Therefore we will only force a reset for versions 92 - 94.
694d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) ||
695bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    (fromVersion >= 92 && fromVersion <= 94)) {
696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
699d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            fromVersion = 63;
700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
722cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
7239ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup");
7249ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup");
7259ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup");
7269ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup");
727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
757b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
777216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                       "_data TEXT UNIQUE NOT NULL," +
778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
81795ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
823acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album) AS number_of_albums, " +
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
837acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
838acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
839acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
840acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
841acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
85978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                // Contains mappings between audio genres and audio files
860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
950acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
951acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.0 shipped with database version 64
952acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
953acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
959403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // In version 66, originally we updateBucketNames(db, "images"),
960403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // but we need to do it in version 89 and therefore save the update here.
961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
972403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
973403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // In version 68, originally we updateBucketNames(db, "video"),
974403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // but we need to do it in version 89 and therefore save the update here.
975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
98595ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
995e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
996e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
99995ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
1001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
1005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
1006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
1007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
1013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
101495ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1015acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1016acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.5 shipped with database version 72
1017acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1018acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
10198d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
10208d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
10218d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
10228d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
10238d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
10248d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
10258d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
10268d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
10278d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
10288d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
10298d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
10308d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
10318d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
10328d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
10338d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
1034a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1035a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
1036a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
1037a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
1038a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
1039a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
1040a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
1041a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
1042a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1043a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
1044a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1045a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
1046a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1047a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
1048a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1049a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
1050a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
1051a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
1052a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
1053a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
1054a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
1055a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
1056e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
1057a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1058a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1059a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
1060a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1061a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1062a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1063a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
1064a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1065a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1066a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1067a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
1068a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
1069a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
1070e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
1071a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1072a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
1073a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
1074a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1075a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1076a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
1077a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
1078a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1079a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1080a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1081a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
1082a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
1083a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
1084a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
1085a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
1086a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
1087a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
108859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
108959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
109095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
109159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
109259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
109359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
109459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
109515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
109615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
109715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
109815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
109915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
110015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
110115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
110215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
110315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
110415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
110515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
110615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
110715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
110815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
1109b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1110acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1111acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.6 shipped with database version 76
1112acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1113acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1114b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
1115b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
1116b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
1117b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
1118b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
1119b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
1120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
1121b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
1122b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
1123b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
1124b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1125b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
1126b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1127b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
1128b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
1129b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
1130b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
1131b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
11321769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
1133acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1134acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.0 and 2.0.1 shipped with database version 77
1135acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1136acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
11371769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
1138044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
11391769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
11401769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
11411769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
1142268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
1143acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1144acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.1 shipped with database version 78
1145acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1146acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1147268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
1148268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
1149268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
1150268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
1151268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
11529be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs";
11539be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER;
1154268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
1155268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
1156268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
1157268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
1158268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
1159268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
1160268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
1161268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
1162268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
1163268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
1164268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
1165268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
1166044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
1167044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
1168044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
1169044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
1170044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
11710ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
1172166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen        if (fromVersion < 81 && !internal) {
11730ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete entries starting with /mnt/sdcard. This is for the benefit
11740ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // of users running builds between 2.0.1 and 2.1 final only, since
11750ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // users updating from 2.0 or earlier will not have such entries.
11760ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
11770ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // First we need to update the _data fields in the affected tables, since
11780ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // otherwise deleting the entries will also delete the underlying files
11790ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // (via a trigger), and we want to keep them.
11800ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11810ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11820ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11830ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11840ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11850ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
1186216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11870ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Once the paths have been renamed, we can safely delete the entries
11880ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';");
11890ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM images WHERE _data IS '////';");
11900ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM video WHERE _data IS '////';");
11910ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';");
11920ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';");
11930ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_meta WHERE _data  IS '////';");
11940ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data  IS '////';");
11950ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
11960ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // rename existing entries starting with /sdcard to /mnt/sdcard
11970ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta" +
11980ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11990ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists" +
12000ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12010ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images" +
12020ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12030ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video" +
12040ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12050ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails" +
12060ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12070ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails" +
12080ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12090ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art" +
12100ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12110ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12120ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete albums and artists, then clear the modification time on songs, which
12130ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // will cause the media scanner to rescan everything, rebuilding the artist and
12140ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // album tables along the way, while preserving playlists.
12150ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // We need this rescan because ICU also changed, and now generates different
12160ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // collation keys
12170ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from albums");
12180ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from artists");
12190ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
12200ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen        }
122184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen
122284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        if (fromVersion < 82) {
1223acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // recreate this view with the correct "group by" specifier
122484403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("DROP VIEW IF EXISTS artist_info");
122584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
122684403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "SELECT artist_id AS _id, artist, artist_key, " +
1227acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album_key) AS number_of_albums, " +
122884403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
122984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "GROUP BY artist_key;");
123084403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        }
1231216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1232acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /* we skipped over version 83, and reverted versions 84, 85 and 86 */
1233ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen
1234ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        if (fromVersion < 87) {
1235ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // The fastscroll thumb needs an index on the strings being displayed,
1236ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // otherwise the queries it does to determine the correct position
1237ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // becomes really inefficient
1238022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);");
1239022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);");
1240022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);");
1241ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        }
1242216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1243acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        if (fromVersion < 88) {
1244acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // Clean up a few more things from versions 84/85/86, and recreate
1245acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // the few things worth keeping from those changes.
1246acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update1;");
1247acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update2;");
1248acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update3;");
1249acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update4;");
1250acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update1;");
1251acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update2;");
1252acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update3;");
1253acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update4;");
125416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP VIEW IF EXISTS album_artists;");
1255acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);");
1256acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);");
1257acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
1258acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
1259acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
1260acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
1261acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        }
1262403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1263fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // In version 89, originally we updateBucketNames(db, "images") and
1264fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames
1265fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        //  for all files and therefore can save the update here.
1266b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1267b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (fromVersion < 91) {
1268bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // Never query by mini_thumb_magic_index
1269bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index");
1270bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1271bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // sort the items by taken date in each bucket
1272bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)");
1273bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)");
1274bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        }
1275bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1276a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
1277d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // Gingerbread ended up going to version 100, but didn't yet have the "files"
1278d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // table, so we need to create that if we're at 100 or lower. This means
1279d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // we won't be able to upgrade pre-release Honeycomb.
1280d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        if (fromVersion <= 100) {
1281afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Remove various stages of work in progress for MTP support
1282afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
1283afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS files");
128416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;");
128516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;");
128616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;");
128716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;");
1288afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;");
1289afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;");
1290afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;");
1291afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;");
1292afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;");
1293afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1294afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create a new table to manage all files in our storage.
1295afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // This contains a union of all the columns from the old
1296afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // images, audio_meta, videos and audio_playlist tables.
1297afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TABLE files (" +
129800a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen                        "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1299afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data TEXT," +     // this can be null for playlists
1300afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_size INTEGER," +
1301afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "format INTEGER," +
1302afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "parent INTEGER," +
1303afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_added INTEGER," +
1304afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified INTEGER," +
1305afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mime_type TEXT," +
1306afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title TEXT," +
1307afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "description TEXT," +
1308afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_display_name TEXT," +
1309afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1310afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images
1311afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "picasa_id TEXT," +
1312afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "orientation INTEGER," +
1313afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1314afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images and video
1315afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "latitude DOUBLE," +
1316afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "longitude DOUBLE," +
1317afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken INTEGER," +
1318afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic INTEGER," +
1319afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_id TEXT," +
1320afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_display_name TEXT," +
1321afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "isprivate INTEGER," +
1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio
1324afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title_key TEXT," +
1325afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist_id INTEGER," +
1326afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album_id INTEGER," +
1327afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "composer TEXT," +
1328afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track INTEGER," +
1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "year INTEGER CHECK(year!=0)," +
1330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_ringtone INTEGER," +
1331afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_music INTEGER," +
1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_alarm INTEGER," +
1333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_notification INTEGER," +
1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_podcast INTEGER," +
1335d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        "album_artist TEXT," +
1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio and video
1338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "duration INTEGER," +
1339afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark INTEGER," +
1340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for video
1342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist TEXT," +
1343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album TEXT," +
1344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "resolution TEXT," +
1345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "tags TEXT," +
1346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category TEXT," +
1347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "language TEXT," +
1348afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_data TEXT," +
1349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for playlists
1351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "name TEXT," +
1352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // media_type is used by the views to emulate the old
1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // images, audio_meta, videos and audio_playlist tables.
1355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "media_type INTEGER," +
1356afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1357afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Value of _id from the old media table.
1358afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Used only for updating other tables during database upgrade.
1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "old_id INTEGER" +
1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       ");");
1361d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen
1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX path_index ON files(_data);");
1363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Copy all data from our obsolete tables to the new files table
136692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
136792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Copy audio records first, preserving the _id column.
136892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We do this to maintain compatibility for content Uris for ringtones.
136992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Unfortunately we cannot do this for images and videos as well.
137092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We choose to do this for the audio table because the fragility of Uris
137192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // for ringtones are the most common problem we need to avoid.
137292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" +
137392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO +
137492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " FROM audio_meta;");
137592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
1376bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT "
1377bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;");
1378bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT "
1379bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;");
138016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            if (!internal) {
1381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT "
1382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST
1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + " FROM audio_playlists;");
1384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
138516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Delete the old tables
1387afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS images");
1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_meta");
1389afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS video");
1390afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
139116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1392afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create views to replace our old tables
1393bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 +
1394afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1396d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 +
1397d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1398d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1399bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 +
1400afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1401afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1402afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1403afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS +
1404afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1405afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_PLAYLIST + ";");
140616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            }
140736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
14089491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // create temporary index to make the updates go faster
14099491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("CREATE INDEX tmp ON files(old_id);");
14109491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1411afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update the image_id column in the thumbnails table.
1412afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files "
1413afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = thumbnails.image_id AND files.media_type = "
1414afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ");");
141536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1416afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1417d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // update audio_id in the audio_genres_map table, and
1418d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // audio_playlists_map tables and playlist_id in the audio_playlists_map table
1419afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files "
1420afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = "
1421afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_AUDIO + ");");
1422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files "
1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_playlists_map.audio_id "
1424afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");");
1425d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files "
1426d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "WHERE files.old_id = audio_playlists_map.playlist_id "
1427d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");");
1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
1429afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1430afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update video_id in the videothumbnails table.
1431afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files "
1432afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = "
1433afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ");");
1434afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
14359491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // we don't need this index anymore now
14369491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("DROP INDEX tmp;");
14379491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1438afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update indices to work on the files table
1439afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS title_idx");
1440afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS album_id_idx");
1441afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS image_bucket_index");
1442afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS video_bucket_index");
1443afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS sort_index");
1444afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS titlekey_index");
1445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS artist_id_idx");
1446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX title_idx ON files(title);");
1447afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1448afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);");
1449afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1450afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1451afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Recreate triggers for our obsolete tables on the new files table
1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
1456afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
1458afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
145936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1460afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " +
1461afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " +
146236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
1464afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
146536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
146636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1467afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " +
146849dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " +
146936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1470afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
147136339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
147236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1474afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " +
1475afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " +
1476afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1477afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
1478afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
1479afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1480afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1481afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " +
1482afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " +
1483afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1485afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "SELECT _DELETE_FILE(old._data);" +
1486afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1488afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
148936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "BEGIN " +
1490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from files where _id=old._id;" +
1491afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_playlists_map where audio_id=old._id;" +
1492afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_genres_map where audio_id=old._id;" +
149336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "END");
149436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood            }
149536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood        }
149636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1497fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (fromVersion < 300) {
1498fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            // we now compute bucket and display names for all files to avoid problems with files
1499fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            // that the media scanner might not recognize as images or videos
1500fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            updateBucketNames(db, "files");
1501fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
1502fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood
1503db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (fromVersion < 301) {
1504db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("DROP INDEX IF EXISTS bucket_index");
1505db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)");
1506db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)");
1507db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1508db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin
150920405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        if (fromVersion < 302) {
151020405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX parent_index ON files(parent);");
151120405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX format_index ON files(format);");
151220405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        }
151320405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood
15142658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        if (fromVersion < 303) {
15152658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // the album disambiguator hash changed, so rescan songs and force
15162658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // albums to be updated. Artists are unaffected.
15172658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("DELETE from albums");
15182658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15192658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15202658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        }
15212658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
15224b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 304 && !internal) {
152351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            // notifies host when files are deleted
152451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
152551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "BEGIN " +
152651d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                        "SELECT _OBJECT_REMOVED(old._id);" +
152751d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "END");
152851d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
152951d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood        }
153051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
15314b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 305 && internal) {
15324b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            // version 304 erroneously added this trigger to the internal database
15334b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
15344b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        }
15354b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood
1536fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen        if (fromVersion < 306 && !internal) {
1537efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // The genre list was expanded and genre string parsing was tweaked, so
1538efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // rebuild the genre list
1539efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
1540efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
1541efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
1542efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
1543efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen        }
1544efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen
154565587f9c204abb2d179527dfdca009f4780e9743Ray Chen        if (fromVersion < 307 && !internal) {
154665587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or
154765587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // EXIF local time.
154865587f9c204abb2d179527dfdca009f4780e9743Ray Chen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
154965587f9c204abb2d179527dfdca009f4780e9743Ray Chen                    + FileColumns.MEDIA_TYPE_IMAGE + ";");
155065587f9c204abb2d179527dfdca009f4780e9743Ray Chen        }
155165587f9c204abb2d179527dfdca009f4780e9743Ray Chen
15524f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // Database version 401 did not add storage_id to the internal database.
15534f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // We need it there too, so add it in version 402
15544f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        if (fromVersion < 401 || (fromVersion == 401 && internal)) {
15559be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Add column for MTP storage ID
15569be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;");
15579be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Anything in the database before this upgrade step will be in the primary storage
15589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";");
15599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
15609be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
156178b2885edc406273d688536b0eadfea006b20662Marco Nelissen        if (fromVersion < 403 && !internal) {
156278b2885edc406273d688536b0eadfea006b20662Marco Nelissen            db.execSQL("CREATE VIEW audio_genres_map_noid AS " +
156378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    "SELECT audio_id,genre_id from audio_genres_map;");
156478b2885edc406273d688536b0eadfea006b20662Marco Nelissen        }
156578b2885edc406273d688536b0eadfea006b20662Marco Nelissen
15669289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        if (fromVersion < 404) {
15679289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // There was a bug that could cause distinct same-named albums to be
15689289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // combined again. Delete albums and force a rescan.
15699289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("DELETE from albums");
15709289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15719289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15729289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        }
15739289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen
1574957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        if (fromVersion < 405) {
1575957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            // Add is_drm column.
1576957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;");
1577957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1578957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("DROP VIEW IF EXISTS audio_meta");
1579957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 +
1580957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1581957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1582957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1583957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            recreateAudioView(db);
1584957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        }
1585957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1586b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (fromVersion < 407) {
15877ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            // Rescan files in the media database because a new column has been added
1588b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // in table files in version 405 and to recover from problems populating
1589b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // the genre tables
15907ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            db.execSQL("UPDATE files SET date_modified=0;");
15917ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang        }
15927ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang
1593bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        if (fromVersion < 408) {
1594bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Add the width/height columns for images and video
1595bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;");
1596bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;");
1597bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1598bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Rescan files to fill the columns
1599bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("UPDATE files SET date_modified=0;");
1600bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1601bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Update images and video views to contain the width/height columns
1602bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS images");
1603bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS video");
1604bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS +
1605bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1606bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1607bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS +
1608bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1609bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1610bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        }
1611bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
16125809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        if (fromVersion < 409 && !internal) {
16135809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // A bug that prevented numeric genres from being parsed was fixed, so
16145809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // rebuild the genre list
16155809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16165809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16175809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
16185809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
16195809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        }
16205809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen
1621166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        if (fromVersion < 500) {
1622166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1623166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;");
1624166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        }
1625a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        if (fromVersion < 501) {
1626a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1627a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // the images_cleanup trigger would delete the image file and the entry
1628a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // in the thumbnail table, which in turn would trigger thumbnails_cleanup
1629a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // to delete the thumbnail image
1630a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup;");
1631a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup;");
1632a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        }
16335afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        if (fromVersion < 502) {
16345afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
16355afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup;");
16365afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        }
16375118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        if (fromVersion < 503) {
16385118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            // genre and playlist cleanup now done in mediaprovider code, instead of in a trigger
16395118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
16405118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
16415118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        }
164290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (fromVersion < 504) {
164390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            // add an index to help with case-insensitive matching of paths
164490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            db.execSQL(
164590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    "CREATE INDEX IF NOT EXISTS path_index_lower ON files(_data COLLATE NOCASE);");
164690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
1647acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sanityCheck(db, fromVersion);
16481d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    }
16491d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen
16501d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    /**
1651216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * Perform a simple sanity check on the database. Currently this tests
1652216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * whether all the _data entries in audio_meta are unique
1653216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     */
1654216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen    private static void sanityCheck(SQLiteDatabase db, int fromVersion) {
1655216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c1 = db.query("audio_meta", new String[] {"count(*)"},
1656216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1657216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"},
1658216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1659216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.moveToFirst();
1660216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.moveToFirst();
1661216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num1 = c1.getInt(0);
1662216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num2 = c2.getInt(0);
1663216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.close();
1664216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.close();
1665216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        if (num1 != num2) {
1666216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            Log.e(TAG, "audio_meta._data column is not unique while upgrading" +
1667216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                    " from schema " +fromVersion + " : " + num1 +"/" + num2);
1668216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            // Delete all audio_meta rows so they will be rebuilt by the media scanner
1669216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("DELETE FROM audio_meta;");
1670216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        }
1671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
1674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
1675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note that views are read-only, so we define a trigger to allow deletes.
1676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
1677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
1678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
1679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
1680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
168195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the bucket_id and
1684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * bucket_display_name columns are correct.
1685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateBucketNames(SQLiteDatabase db, String tableName) {
1689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
1690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
1693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
16979491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                String [] rowId = new String[1];
1698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
17009491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                    rowId[0] = String.valueOf(cursor.getInt(idColumnIndex));
1701d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    if (data != null) {
1702d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        ContentValues values = new ContentValues();
1703d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        computeBucketValues(data, values);
17049491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                        db.update(tableName, values, "_id=?", rowId);
1705d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    } else {
1706d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        Log.w(TAG, "null data at id " + rowId);
1707d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    }
1708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
1720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
1721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
1725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
1726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
1729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
1734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
1735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
1737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
1738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
1739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
1740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
1741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
1742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
1743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
1756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
1760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
1761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
1762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
1763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
1766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
1767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
1768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
1769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
1770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
1772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
1773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
1774d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood        values.put(ImageColumns.BUCKET_ID, path.hashCode());
1775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
1776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
1781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
1784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
1785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
1786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
1787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
1788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
1790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1792b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
1793498b62c2912302a23532c73a028a7684c5df33caRay Chen     * Copy taken time from date_modified if we lost the original value (e.g. after factory reset)
1794498b62c2912302a23532c73a028a7684c5df33caRay Chen     * This works for both video and image tables.
1795b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     *
1796b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     * @param values the content values, where taken time is updated.
1797b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     */
1798b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    private static void computeTakenTime(ContentValues values) {
1799b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        if (! values.containsKey(Images.Media.DATE_TAKEN)) {
1800b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // This only happens when MediaScanner finds an image file that doesn't have any useful
1801b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // reference to get this value. (e.g. GPSTimeStamp)
1802498b62c2912302a23532c73a028a7684c5df33caRay Chen            Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED);
1803b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            if (lastModified != null) {
1804b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                values.put(Images.Media.DATE_TAKEN, lastModified * 1000);
1805b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            }
1806b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        }
1807b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    }
1808b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen
1809b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    /**
1810b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
1811b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
1812b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
1813b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
1814b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
1815b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
1816b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
1817b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
1818b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
1819b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1820b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
1821b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1822b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
1823e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen            long id = c.getLong(0);
1824b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
1825b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
1826b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
18279299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            MediaThumbRequest req = requestMediaThumbnail(path, origUri,
18289299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    MediaThumbRequest.PRIORITY_HIGH, magic);
18299299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            if (req == null) {
18309299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                return false;
18319299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            }
18329299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            synchronized (req) {
18339299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                try {
18349299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    while (req.mState == MediaThumbRequest.State.WAIT) {
18359299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        req.wait();
183620434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
18379299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                } catch (InterruptedException e) {
18389299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    Log.w(TAG, e);
18399299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                }
18409299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                if (req.mState == MediaThumbRequest.State.DONE) {
18419299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    result = true;
1842b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1843b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
1844b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1845b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
1846b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1847b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
1848b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1849b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1850e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
1851e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
1852e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
1853e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
1854e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
1855e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
1856e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
1857e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
1858e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
1859e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
1860b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
1861b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
1862b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
1863b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
1864b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
1865b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
1866b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1867b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
1868b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1869b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1870b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
1871b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
1872b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
1873b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
1874b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1875b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1876b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1877b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
187820434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
1879e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
1880e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
1881e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
1882b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1883b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
188420434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
1885b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
188620434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
1887e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
1888e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
188920434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
189020434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
1891e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
1892e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
189320434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
189420434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
1895e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
189620434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
189720434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
189820434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
189920434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1900e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
190120434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
1902e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
1903e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
190420434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
190520434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
190620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
190720434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
190820434e032e498b716f87cce2f23dd646819218bfRay Chen                }
190920434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
1910e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
191120434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
191220434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
191320434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
191420434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
191520434e032e498b716f87cce2f23dd646819218bfRay Chen
191620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
191720434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
191820434e032e498b716f87cce2f23dd646819218bfRay Chen                }
191920434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1920b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1921b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1922b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
1923b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
1924b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1925b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
1926b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1927b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
1928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
1930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
1931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
1932baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        List<String> prependArgs = new ArrayList<String>();
1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
193401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
1938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
19410027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
19420027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
19430027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
1944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
19470027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
19480027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
19490027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
19500027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
19510027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
19520027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
19530027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
19540027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
19550027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
1956704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        if (table == VERSION) {
1957704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"version"});
195890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            c.addRow(new Integer[] {getDatabaseVersion(getContext())});
1959704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            return c;
1960704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        }
1961704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen
1962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
196310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
196410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
1965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
1966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
196710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
196810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getReadableDatabase();
19695fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) return null;
1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
19714574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
1972c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String filter = uri.getQueryParameter("filter");
1973c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String [] keywords = null;
1974c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        if (filter != null) {
1975c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            filter = Uri.decode(filter).trim();
1976c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            if (!TextUtils.isEmpty(filter)) {
1977c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                String [] searchWords = filter.split(" ");
1978c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                keywords = new String[searchWords.length];
1979c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                Collator col = Collator.getInstance();
1980c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                col.setStrength(Collator.PRIMARY);
1981c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; i < searchWords.length; i++) {
1982c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    String key = MediaStore.Audio.keyFor(searchWords[i]);
1983c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("\\", "\\\\");
1984c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("%", "\\%");
1985c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("_", "\\_");
1986c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    keywords[i] = key;
1987c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
1988c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            }
1989c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        }
1990db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (uri.getQueryParameter("distinct") != null) {
1991db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            qb.setDistinct(true);
1992db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1993c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen
1994b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
1995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
1997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2013baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2014baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2018b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2019b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
2020b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
2021b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2022b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2026d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2027ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
2028ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                          || selection.equalsIgnoreCase("is_podcast=1") )
2029c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2030c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2031ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting songs");
2032ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2033ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2034ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio");
2035c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2036c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2037c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2038c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2039c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2040c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2041baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'");
2042baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2043c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2044ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
2049baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2050baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
2056baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_genres_map WHERE audio_id=?)");
2057baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2062baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2063baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
2069baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_playlists_map WHERE audio_id=?)");
2070baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2071702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2075baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2076baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2085baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2086baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2089bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen            case AUDIO_GENRES_ALL_MEMBERS:
2090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
209178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                {
209278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // if simpleQuery is true, we can do a simpler query on just audio_genres_map
209378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // we can do this if we have no keywords and our projection includes just columns
209478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // from audio_genres_map
209578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    boolean simpleQuery = (keywords == null && projectionIn != null
209678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            && (selection == null || selection.equalsIgnoreCase("genre_id=?")));
209778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (projectionIn != null) {
209878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; i < projectionIn.length; i++) {
209978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            String p = projectionIn[i];
210078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (p.equals("_id")) {
210178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // note, this is different from playlist below, because
210278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // "_id" used to (wrongly) be the audio id in this query, not
210378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // the row id of the entry in the map, and we preserve this
210478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // behavior for backwards compatibility
210578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
210678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
210778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (simpleQuery && !(p.equals("audio_id") ||
210878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    p.equals("genre_id"))) {
210978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
211078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
211178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
211278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
211378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (simpleQuery) {
211478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid");
2115bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2116baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere("genre_id=?");
2117baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2118bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
211978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    } else {
212078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid, audio");
2121bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        qb.appendWhere("audio._id = audio_id");
2122bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2123baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere(" AND genre_id=?");
2124baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2125bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
212678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; keywords != null && i < keywords.length; i++) {
212778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(" AND ");
212878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
212978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.ALBUM_KEY +
213078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.TITLE_KEY +
2131baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                    " LIKE ? ESCAPE '\\'");
2132baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add("%" + keywords[i] + "%");
213378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
213478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
213578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                }
2136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2144baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2145baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2148b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2150e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // if simpleQuery is true, we can do a simpler query on just audio_playlists_map
2151e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // we can do this if we have no keywords and our projection includes just columns
2152e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // from audio_playlists_map
21534382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                boolean simpleQuery = (keywords == null && projectionIn != null
21544382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                        && (selection == null || selection.equalsIgnoreCase("playlist_id=?")));
215597e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
215697e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
2157e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        String p = projectionIn[i];
2158e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (simpleQuery && !(p.equals("audio_id") ||
2159e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                p.equals("playlist_id") || p.equals("play_order"))) {
2160e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                            simpleQuery = false;
2161e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        }
2162e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (p.equals("_id")) {
216397e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
216497e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
2165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2167e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                if (simpleQuery) {
2168e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map");
2169baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("playlist_id=?");
2170baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2171e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                } else {
2172e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map, audio");
2173baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("audio._id = audio_id AND playlist_id=?");
2174baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2175e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2176e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(" AND ");
2177e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2178e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2179e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.TITLE_KEY +
2180baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2181baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2182e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    }
2183c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2184b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) {
2185baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere(" AND audio_playlists_map._id=?");
2186baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(5));
2187b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                }
2188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2195baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2196baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2199b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
2200b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2201b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
2202b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
2203b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2204b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2205b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2206b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
2208d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2209ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2210c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2211c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2212ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting artists");
2213ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2214ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct artist_id)";
2215ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2216ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2217ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("artist_info");
2218c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2219c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2220c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2221c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2222c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2223baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2224baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2225c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2226ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
2230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
2231baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2232baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
2236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
2237acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
2238acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        " audio.album_id=album_art.album_id");
2239acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
2240baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "artists_albums_map WHERE artist_id=?)");
2241baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(aid);
2242c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; keywords != null && i < keywords.length; i++) {
2243c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(" AND ");
2244c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2245c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            "||" + MediaStore.Audio.Media.ALBUM_KEY +
2246baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            " LIKE ? ESCAPE '\\'");
2247baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add("%" + keywords[i] + "%");
2248c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2249acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                groupBy = "audio.album_id";
2250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
2251acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
2252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
2253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
2254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
2257d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2258ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2259c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2260c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2261ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting albums");
2262ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2263ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct album_id)";
2264ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2265ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2266ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("album_info");
2267c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2268c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2269c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2270c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2271c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2272c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2273baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2274baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2275c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2276ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
2280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
2281baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2282baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
2286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
2287baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("album_id=?");
2288baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2291a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
2292a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
2293a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
2294a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
2295a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
2296baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection,
2297baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        combine(prependArgs, selectionArgs), sort, table, limit);
2298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
229916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
2300e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
2301baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2302baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(2));
2303b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                // fall through
230416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
2305e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
230616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                qb.setTables("files");
2307b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                break;
2308b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2309e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            case MTP_OBJECT_REFERENCES:
2310e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int handle = Integer.parseInt(uri.getPathSegments().get(2));
231110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                return getObjectReferences(helper, db, handle);
2312e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
2315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2317baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection,
2318baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        //        combine(prependArgs, selectionArgs), groupBy, null, sort, limit));
2319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
2320baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
2321b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
2323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            c.setNotificationUri(getContext().getContentResolver(), uri);
2324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2325b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
2327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2329baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    private String[] combine(List<String> prepend, String[] userArgs) {
2330baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int presize = prepend.size();
2331baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        if (presize == 0) {
2332baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            return userArgs;
2333baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2334baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2335baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int usersize = (userArgs != null) ? userArgs.length : 0;
2336baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        String [] combined = new String[presize + usersize];
2337baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < presize; i++) {
2338baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[i] = prepend.get(i);
2339baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2340baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < usersize; i++) {
2341baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[presize + i] = userArgs[i];
2342baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2343baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        return combined;
2344baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    }
2345baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
2347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
23484574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
23494574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
2350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
235118c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen        String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment();
2352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
2353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
2356a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
2357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Collator col = Collator.getInstance();
2358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        col.setStrength(Collator.PRIMARY);
2359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
2360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
2361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
2362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
23633001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            String key = MediaStore.Audio.keyFor(searchWords[i]);
23643001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("\\", "\\\\");
23653001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("%", "\\%");
23663001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("_", "\\_");
2367a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
2368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
23693001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                        searchWords[i].equals("the")) ? "%" : "%" + key + "%";
2370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2372a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
2373a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
2374a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
23753001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where = "match LIKE ? ESCAPE '\\'";
2376a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
23773001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where += " AND match LIKE ? ESCAPE '\\'";
2378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2381a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
2382a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
2383a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
2384a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
2385a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
2386a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
2387a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
2388a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
2389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
23904574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
2391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
2395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
2397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2401c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case FILES_ID:
240226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                Cursor c = null;
240326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                try {
240426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    c = query(url, MIME_TYPE_PROJECTION, null, null, null);
240526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null && c.getCount() == 1) {
240626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.moveToFirst();
240726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        String mimeType = c.getString(1);
240826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.deactivate();
240926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        return mimeType;
241026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
241126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                } finally {
241226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null) {
241326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.close();
241426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
2415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
2420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
2421804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen            case AUDIO_ALBUMART_ID:
2422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
2424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
2427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
2429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
2433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
2436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
2439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
2442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
2445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2446804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen        throw new IllegalStateException("Unknown URL : " + url);
2447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
2451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * present a new file is created.
2452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
2454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
2455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
2457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
2458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
2459801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        String file = initialValues.getAsString(MediaStore.MediaColumns.DATA);
2460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
2461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
2462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
2463801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            values.put(MediaStore.MediaColumns.DATA, file);
2464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
2466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!ensureFileExists(file)) {
2469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalStateException("Unable to create new file: " + file);
2470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
2472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2474d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectAdded(long objectHandle) {
247534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
247634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
247734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
247834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectAdded((int)objectHandle);
247934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
248034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectAdded", e);
248134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
248234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2483d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2484d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2485d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2486d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2487d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectRemoved(long objectHandle) {
248834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
248934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
249034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
249134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectRemoved((int)objectHandle);
249234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
249334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectRemoved", e);
249434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
249534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2496d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2497d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2498d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2499d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
2502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
2504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
2505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
250610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
250710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
251110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
25125fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) {
25135fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            throw new IllegalStateException("Couldn't open database for " + uri);
25145fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        }
2515ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2516ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
2517ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
2518e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } else if (match == MTP_OBJECT_REFERENCES) {
2519e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            int handle = Integer.parseInt(uri.getPathSegments().get(2));
252010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            return setObjectReferences(helper, db, handle, values);
2521ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2522ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
2524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
2525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
2527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
2528801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                if (values[i] != null) {
2529801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                    insertInternal(uri, match, values[i]);
2530801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                }
2531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
2533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
2534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
2536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
2539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2542801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood    public Uri insert(Uri uri, ContentValues initialValues) {
25435d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int match = URI_MATCHER.match(uri);
25445d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        Uri newUri = insertInternal(uri, match, initialValues);
25455d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // do not signal notification for MTP objects.
25465d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // we will signal instead after file transfer is successful.
2547e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        if (newUri != null && match != MTP_OBJECTS) {
2548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
2551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2553ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
2554ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
2555ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
25568b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
25578b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
25588b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
25598b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
2560ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2561ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
2562ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
2563ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
2564ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
2565ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
25668b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
25678b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
25688b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
25698b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
25708b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
25718b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
25728b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
25738b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
25748b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
25758b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
25768b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
25778b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
25788b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
2579ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
2580ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
2581ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
2582ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
2583ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
2584ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
2585ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2586ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
2587ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
2588ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
2589ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
259010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) {
259110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path);
2592ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        ContentValues values = new ContentValues();
2593ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION);
2594ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.DATA, path);
259510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        values.put(FileColumns.PARENT, getParent(helper, db, path));
25969be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        values.put(FileColumns.STORAGE_ID, getStorageId(path));
2597f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        File file = new File(path);
2598f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        if (file.exists()) {
2599f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
2600f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        }
260110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumInserts++;
2602ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2603ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        sendObjectAdded(rowId);
2604ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        return rowId;
2605ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
2606ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
260710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) {
2608b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        int lastSlash = path.lastIndexOf('/');
2609b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (lastSlash > 0) {
2610b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String parentPath = path.substring(0, lastSlash);
26119be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            for (int i = 0; i < mExternalStoragePaths.length; i++) {
26129be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (parentPath.equals(mExternalStoragePaths[i])) {
26139be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return 0;
26149be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
2615b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
26167f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Long cid = mDirectoryCache.get(parentPath);
26177f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            if (cid != null) {
26187f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
26197f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return cid;
26207f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            }
26217f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
2622f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            // Use "LIKE" instead of "=" on case insensitive file systems so we do a
2623f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            // case insensitive match when looking for parent directory.
26247f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: investigate whether a "nocase" constraint on the column and
26257f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // using "=" would give the same result faster.
2626f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            String selection = (mCaseInsensitivePaths ? MediaStore.MediaColumns.DATA + " LIKE ?"
2627a9bb89771934314157dd26253195dc16bddc2cfaDongwon Kang                    // search only directories.
262810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    + " AND format=" + MtpConstants.FORMAT_ASSOCIATION
2629f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood                    : MediaStore.MediaColumns.DATA + "=?");
2630b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String [] selargs = { parentPath };
263110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
26327f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
2633b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            try {
26347f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                long id;
2635b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c == null || c.getCount() == 0) {
2636b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    // parent isn't in the database - so add it
263710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    id = insertDirectory(helper, db, parentPath);
26387f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
2639b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                } else {
2640b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    c.moveToFirst();
26417f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    id = c.getLong(0);
26427f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
2643b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                }
26447f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.put(parentPath, id);
26457f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return id;
2646b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            } finally {
2647b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c != null) c.close();
2648b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
2649b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        } else {
2650b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            return 0;
2651b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
2652b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
2653b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
26549be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private int getStorageId(String path) {
26559be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        for (int i = 0; i < mExternalStoragePaths.length; i++) {
26569be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String test = mExternalStoragePaths[i];
26579be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (path.startsWith(test)) {
26589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int length = test.length();
26599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (path.length() == length || path.charAt(length) == '/') {
26609be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return MtpStorage.getStorageId(i);
26619be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
26629be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
26639be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
26649be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        // default to primary storage
26659be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        return MtpStorage.getStorageId(0);
26669be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    }
26679be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
266810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType,
2669afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            boolean notify) {
267010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
2671afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        ContentValues values = null;
2672afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2673afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        switch (mediaType) {
2674afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_IMAGE: {
267510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".jpg", "DCIM/Camera");
2676afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2677afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2678afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String data = values.getAsString(MediaColumns.DATA);
2679afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
2680afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    computeDisplayName(data, values);
2681afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2682afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2685afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2686afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_AUDIO: {
2687afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // SQLite Views are read-only, so we need to deconstruct this
2688afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // insert and do inserts into the underlying tables.
2689afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // If doing this here turns out to be a performance bottleneck,
2690afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // consider moving this to native code and using triggers on
2691afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the view.
2692afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values = new ContentValues(initialValues);
2693afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
26942658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
26952658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
26962658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                values.remove(MediaStore.Audio.Media.COMPILATION);
26972658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
2698afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Insert the artist into the artist table and remove it from
2699afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the input values
2700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                Object so = values.get("artist");
2701afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String s = (so == null ? "" : so.toString());
2702afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("artist");
2703afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long artistRowId;
270410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> artistCache = helper.mArtistCache;
2705801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String path = values.getAsString(MediaStore.MediaColumns.DATA);
2706afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(artistCache) {
2707afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = artistCache.get(s);
2708afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
270910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        artistRowId = getKeyIdForName(helper, db,
271010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "artists", "artist_key", "artist",
2711afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, s, path, 0, null, artistCache, uri);
2712afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2713afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        artistRowId = temp.longValue();
2714afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2715afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2716afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String artist = s;
2717afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2718afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Do the same for the album field
2719afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.get("album");
2720afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2721afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("album");
2722afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long albumRowId;
272310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> albumCache = helper.mAlbumCache;
2724afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(albumCache) {
27252658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    int albumhash = 0;
27262658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    if (albumartist != null) {
27272658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = albumartist.hashCode();
27282658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else if (compilation != null && compilation.equals("1")) {
27292658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        // nothing to do, hash already set
27302658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else {
27312658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
27322658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    }
2733afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    String cacheName = s + albumhash;
2734afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = albumCache.get(cacheName);
2735afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
273610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        albumRowId = getKeyIdForName(helper, db,
273710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "albums", "album_key", "album",
2738afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, cacheName, path, albumhash, artist, albumCache, uri);
2739afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2740afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        albumRowId = temp;
2741afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2742afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
27435d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
2744afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("artist_id", Integer.toString((int)artistRowId));
2745afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("album_id", Integer.toString((int)albumRowId));
2746afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.getAsString("title");
2747afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2748afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title_key", MediaStore.Audio.keyFor(s));
2749afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // do a final trim of the title, in case it started with the special
2750afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // "sort first" character (ascii \001)
2751afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("title");
2752afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title", s.trim());
2753b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2754801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values);
2755afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2756afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2757afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2758afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_VIDEO: {
275910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".3gp", "video");
2760801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String data = values.getAsString(MediaStore.MediaColumns.DATA);
2761afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeDisplayName(data, values);
2762afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2763afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2764afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2765afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2766afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2767afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (values == null) {
2768afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(initialValues);
2769afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2770fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // compute bucket_id and bucket_display_name for all files
2771afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String path = values.getAsString(MediaStore.MediaColumns.DATA);
2772fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (path != null) {
2773fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            computeBucketValues(path, values);
2774fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
2775fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2776afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2777afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        long rowId = 0;
2778afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer i = values.getAsInteger(
2779afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2780afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (i != null) {
2781afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = i.intValue();
2782afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(values);
2783afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2784afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2785afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2786afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String title = values.getAsString(MediaStore.MediaColumns.TITLE);
27873572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen        if (title == null && path != null) {
2788c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            title = MediaFile.getFileTitle(path);
2789c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2790c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        values.put(FileColumns.TITLE, title);
2791c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2792afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE);
2793afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer formatObject = values.getAsInteger(FileColumns.FORMAT);
2794c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        int format = (formatObject == null ? 0 : formatObject.intValue());
2795c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format == 0) {
279663b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang            if (TextUtils.isEmpty(path)) {
2797c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                // special case device created playlists
2798afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
2799c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST);
2800c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    // create a file path for the benefit of MTP
28019be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    path = mExternalStoragePaths[0]
2802afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            + "/Playlists/" + values.getAsString(Audio.Playlists.NAME);
2803c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(MediaStore.MediaColumns.DATA, path);
280410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    values.put(FileColumns.PARENT, getParent(helper, db, path));
2805c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                } else {
280663b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang                    Log.e(TAG, "path is empty in insertFile()");
2807c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                }
2808c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            } else {
2809c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                format = MediaFile.getFormatCode(path, mimeType);
2810c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
2811c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2812c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format != 0) {
2813c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.FORMAT, format);
2814c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            if (mimeType == null) {
2815c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                mimeType = MediaFile.getMimeTypeForFormatCode(format);
2816c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
2817c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2818c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2819801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        if (mimeType == null && path != null) {
2820c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            mimeType = MediaFile.getMimeTypeForFile(path);
2821c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2822c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (mimeType != null) {
2823c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.MIME_TYPE, mimeType);
2824afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2825f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) {
2826afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int fileType = MediaFile.getFileTypeForMimeType(mimeType);
2827afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (MediaFile.isAudioFileType(fileType)) {
2828afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_AUDIO;
2829afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isVideoFileType(fileType)) {
2830afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_VIDEO;
2831afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isImageFileType(fileType)) {
2832afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_IMAGE;
2833afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isPlayListFileType(fileType)) {
2834afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
2835afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2836afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2837c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2838afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        values.put(FileColumns.MEDIA_TYPE, mediaType);
2839c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2840afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (rowId == 0) {
2841afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
28423572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen                String name = values.getAsString(Audio.Playlists.NAME);
2843282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                if (name == null && path == null) {
2844282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                    // MediaScanner will compute the name from the path if we have one
2845afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
2846282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                            "no name was provided when inserting abstract playlist");
2847afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2848afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            } else {
2849a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu                if (path == null) {
2850afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // path might be null for playlists created on the device
2851afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // or transfered via MTP
2852afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
2853afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "no path was provided when inserting new file");
2854afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2855e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2856b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2857f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            // make sure modification date and size are set
2858f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            if (path != null) {
2859f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                File file = new File(path);
2860f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                if (file.exists()) {
2861f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                    values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
286216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.SIZE, file.length());
2863e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
28645d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
2865b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2866afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            Long parent = values.getAsLong(FileColumns.PARENT);
28675d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (parent == null) {
2868e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
286910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    long parentId = getParent(helper, db, path);
287016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.PARENT, parentId);
2871e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
28729be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
28739be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            Integer storage = values.getAsInteger(FileColumns.STORAGE_ID);
28749be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (storage == null) {
28759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int storageId = getStorageId(path);
28769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                values.put(FileColumns.STORAGE_ID, storageId);
28775d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
2878b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
287910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumInserts++;
2880afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2881afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId);
2882afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
288310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (rowId != -1 && notify) {
2884afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                sendObjectAdded(rowId);
2885afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
28865d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
288710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates++;
288816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.update("files", values, FileColumns._ID + "=?",
2889afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    new String[] { Long.toString(rowId) });
28905d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        }
2891afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2892afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        return rowId;
28931717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    }
28941717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
289510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) {
289610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
2897a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
2898e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
2899e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
2900e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
2901e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
2902afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long playlistId = c.getLong(0);
2903afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
2904afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
2905e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
2906e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return null;
2907e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
290810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumQueries++;
2909e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return db.rawQuery(OBJECT_REFERENCES_QUERY,
2910afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        new String[] { Long.toString(playlistId) } );
2911e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2912e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
2913e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
2914e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
2915e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2916e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2917e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return null;
2918e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
2919e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
292010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db,
292110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            int handle, ContentValues values[]) {
2922e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // first look up the media table and media ID for the object
2923e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        long playlistId = 0;
292410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
2925a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
2926e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
2927e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
2928e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
2929e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
2930afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
2931afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
2932e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
2933e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return 0;
2934e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2935afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                playlistId = c.getLong(0);
2936e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2937e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
2938e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
2939e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
2940e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2941e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2942e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (playlistId == 0) {
2943e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return 0;
2944e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2945e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2946e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // next delete any existing entries
294710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumDeletes++;
294810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        db.delete("audio_playlists_map", "playlist_id=?",
2949e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] { Long.toString(playlistId) });
2950e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2951e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // finally add the new entries
2952e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int count = values.length;
2953e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int added = 0;
2954e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
2955e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        for (int i = 0; i < count; i++) {
2956e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // convert object ID to audio ID
2957e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long audioId = 0;
2958e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID);
295910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
2960a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            c = db.query("files", sMediaTableColumns, "_id=?",
2961e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    new String[] {  Long.toString(objectId) },
2962e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    null, null, null);
2963e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            try {
2964e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null && c.moveToNext()) {
2965afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    int mediaType = c.getInt(1);
296650d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood                    if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) {
2967e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // we only allow audio files in playlists, so skip
2968e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        continue;
2969e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
2970afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    audioId = c.getLong(0);
2971e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2972e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            } finally {
2973e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null) {
2974e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    c.close();
2975e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2976e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2977e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (audioId != 0) {
2978e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                ContentValues v = new ContentValues();
2979e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
2980e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
2981a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added);
2982a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                valuesList[added++] = v;
2983e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2984e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2985e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (added < count) {
2986e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // we weren't able to find everything on the list, so lets resize the array
2987e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // and pass what we have.
2988e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            ContentValues[] newValues = new ContentValues[added];
2989e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            System.arraycopy(valuesList, 0, newValues, 0, added);
2990e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            valuesList = newValues;
2991e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2992e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return playlistBulkInsert(db,
2993e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId),
2994e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList);
2995e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
2996e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2997b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
2998b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres._ID, // 0
2999b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres.NAME, // 1
3000b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    };
3001b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3002b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private void updateGenre(long rowId, String genre) {
3003b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri uri = null;
3004b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Cursor cursor = null;
3005b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri genresUri = MediaStore.Audio.Genres.getContentUri("external");
3006b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        try {
3007b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // see if the genre already exists
3008b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
3009b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            new String[] { genre }, null);
3010b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor == null || cursor.getCount() == 0) {
3011b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre does not exist, so create the genre in the genre table
3012b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                ContentValues values = new ContentValues();
3013b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                values.put(MediaStore.Audio.Genres.NAME, genre);
3014b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = insert(genresUri, values);
3015b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            } else {
3016b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre already exists, so compute its Uri
3017b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.moveToNext();
3018b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0));
3019b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3020b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (uri != null) {
3021b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY);
3022b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3023b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        } finally {
3024b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // release the cursor if it exists
3025b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor != null) {
3026b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.close();
3027b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3028b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3029b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3030b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (uri != null) {
3031b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // add entry to audio_genre_map
3032b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            ContentValues values = new ContentValues();
3033b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
3034b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            insert(uri, values);
3035b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3036b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    }
3037b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
30385d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood    private Uri insertInternal(Uri uri, int match, ContentValues initialValues) {
3039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
3040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3041d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
3042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3044bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
304510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
304610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
304710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
304810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
304910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
305010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStartTime = SystemClock.currentTimeMicro();
305110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
3053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3055b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
305638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String path = null;
3057b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3058b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3059b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
306038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            path = initialValues.getAsString(MediaStore.MediaColumns.DATA);
3061b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3062b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
306338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
306510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
306610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null && match != VOLUMES && match != MTP_CONNECTED) {
3067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
307010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
3071819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null
307210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                : helper.getWritableDatabase());
3073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
3075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
307610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues, FileColumns.MEDIA_TYPE_IMAGE, true);
3077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
3079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3084b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
3085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
308610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3087b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
308810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
3090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
3092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContentUri(uri.getPathSegments().get(0)), rowId);
3093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3097b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
3098b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
309910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3100b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
310110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3102b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
3103b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
3104b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
3105b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            getContentUri(uri.getPathSegments().get(0)), rowId);
3106b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
3107b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3108b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
3109b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
311110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues, FileColumns.MEDIA_TYPE_AUDIO, true);
3112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3114b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    if (genre != null) {
3115b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        updateGenre(rowId, genre);
3116b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    }
3117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
3122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3123bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
312510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3126ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
3127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
3134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3135bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
313710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
3139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
3140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
314710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3148bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                rowId = db.insert("audio_genres", "audio_id", initialValues);
3149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
3151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
3156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
3157bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
315910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
3161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
3168bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
317010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, values, FileColumns.MEDIA_TYPE_PLAYLIST, true);
3171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
3173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
3179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
3180bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
318210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3183ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
3184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
319110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues, FileColumns.MEDIA_TYPE_VIDEO, true);
3192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3193b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
3194b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            uri.getPathSegments().get(0)), rowId);
3195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3199c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case AUDIO_ALBUMART: {
320010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (helper.mInternal) {
3201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
3202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3203bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = null;
3204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
3205bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
3206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
3207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
3208bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = initialValues;
3209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
321010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3211801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
3212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3216c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
3219bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                return attachVolume(initialValues.getAsString("name"));
3220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3221819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            case MTP_CONNECTED:
322234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                synchronized (mMtpServiceConnection) {
322334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    if (mMtpService == null) {
322434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        Context context = getContext();
322534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        // MTP is connected, so grab a connection to MtpService
322634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        context.bindService(new Intent(context, MtpService.class),
322734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
322834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    }
322934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
3230819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                break;
3231819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
3232afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FILES:
323310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
3234bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                        FileColumns.MEDIA_TYPE_NONE, true);
3235fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                if (rowId > 0) {
3236fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                    newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId);
323710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (initialValues.getAsInteger(MediaStore.Files.FileColumns.FORMAT)
323810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            == MtpConstants.FORMAT_ASSOCIATION) {
323938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        mDirectoryCache.put(path, rowId);
324010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
3241fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                }
3242fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                break;
3243fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood
3244e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
3245afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // don't send a notification if the insert originated from MTP
324610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
3247bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                        FileColumns.MEDIA_TYPE_NONE, false);
3248afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (rowId > 0) {
3249afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId);
32505d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                }
32515d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                break;
32525d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
3255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
325738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
325838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            // need to set the media_type of all the files below this folder to 0
325938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processNewNoMediaPath(helper, db, path);
326038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
3261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
3262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
326438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
326538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Sets the media type of all files below the newly added .nomedia file or
326638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * hidden folder to 0, so the entries no longer appear in e.g. the audio and
326738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * images views.
326838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     *
326938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * @param path The path to the new .nomedia file or hidden directory
327038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
327138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private void processNewNoMediaPath(DatabaseHelper helper, SQLiteDatabase db, String path) {
327238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        File nomedia = new File(path);
327338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (nomedia.exists()) {
327438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            // only do this if the file actually exists, so we don't get tricked
327538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            // by someone just inserting a .nomedia entry into the database
327638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent();
327738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            ContentValues mediatype = new ContentValues();
327838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mediatype.put("media_type", 0);
327938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            int numrows = db.update("files", mediatype, "_data LIKE ?", new String[] { hiddenroot  + "/%"});
328038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper.mNumUpdates += numrows;
328138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            ContentResolver res = getContext().getContentResolver();
328238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
328338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
328438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
328538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
328638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
328738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Rescan files for missing metadata and set their type accordingly.
328838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * There is code for detecting the removal of a nomedia file or renaming of
328938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * a directory from hidden to non-hidden in the MediaScanner and MtpDatabase,
329038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * both of which call here.
329138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
329238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private void processRemovedNoMediaPath(final String path) {
329338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        final DatabaseHelper helper;
329438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path.startsWith(mExternalStoragePaths[0])) {
329538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
329638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        } else {
329738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
329838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
329938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
330038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        new ScannerClient(getContext(), db, path);
330138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
330238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
330338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private static final class ScannerClient implements MediaScannerConnectionClient {
330438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String mPath = null;
330538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        MediaScannerConnection mScannerConnection;
330638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase mDb;
330738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
330838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public ScannerClient(Context context, SQLiteDatabase db, String path) {
330938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mDb = db;
331038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mPath = path;
331138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection = new MediaScannerConnection(context, this);
331238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.connect();
331338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
331438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
331538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
331638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onMediaScannerConnected() {
331738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            Cursor c = mDb.query("files", openFileColumns, "_data like ?",
331838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    new String[] { mPath + "/%"}, null, null, null);
331938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            while (c.moveToNext()) {
332038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                String d = c.getString(0);
332138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(d);
332238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (f.isFile()) {
332338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    mScannerConnection.scanFile(d, null);
332438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                }
332538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            }
332638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.disconnect();
332738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            c.close();
332838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
332938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
333038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
333138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onScanCompleted(String path, Uri uri) {
333238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
333338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
333438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3335cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
3336cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
3337cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
3338cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3339cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
3340cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
3341cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
3342cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
3343cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
3344cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
3345cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
3346cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
3347cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
3348cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
3349cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3350cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
3351cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
3352cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
3353cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3354cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
3355cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3356cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
3357cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
3358cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
3359cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
3360cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
3361cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
3362cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
3363cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
3364cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3365cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
3366cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3367cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3368cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
3369cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3370cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
33719299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
3372b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
3373e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
3374e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
3375e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
33769299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
3377e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
3378e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
3379e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
3380e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
3381e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
3382e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
3383e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
3384b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
3385b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
3386b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
3387b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
3390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
3391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
3392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
3394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
3395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
3396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
3397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
33989be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension;
3399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
3403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
3404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
3405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
3406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
3408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
3409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
3410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
3411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
3412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
3413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
3414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
341517ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood            file.getParentFile().mkdirs();
3416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
341717ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood                return file.createNewFile();
3418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
3419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
3420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
3426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
3427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
3428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
3431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
3432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
3434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
3435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
3436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
34379f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
3438afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3439afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE;
34409f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
34419f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
3442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
3443afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
3445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3447b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
3448b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3449b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
3450b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
3451b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3452b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
3454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO;
3456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
3459afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
3464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
3469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
3472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
3473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
3475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
3480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
3483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
3490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
3495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
3497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
3500afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3501afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST;
3502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3505afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
3510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
3512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
3517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
3518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
3521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
3522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
3523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
3526afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3527afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO;
3528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
3531afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3535b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
3536b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3537b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
3538b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
3539b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3540b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
354116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
3542e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
35431717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                where = "_id=" + uri.getPathSegments().get(2);
354416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
3545e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
354616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                out.table = "files";
35471717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                break;
35481717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
3549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
3552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
3555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
3556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
3557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
3558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
3560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
3563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
3568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
3570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
3574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
3575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
357610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
357710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
357810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
357910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
358010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
358110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStopTime = SystemClock.currentTimeMicro();
358210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
3584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
3585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3587819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        if (match == VOLUMES_ID) {
3588819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            detachVolume(uri);
3589819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            count = 1;
3590819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else if (match == MTP_CONNECTED) {
359134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (mMtpServiceConnection) {
359234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                if (mMtpService != null) {
359334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // MTP has disconnected, so release our connection to MtpService
359434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    getContext().unbindService(mMtpServiceConnection);
359534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 1;
359634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // mMtpServiceConnection.onServiceDisconnected might not get called,
359734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // so set mMtpService = null here
359834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
359934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } else {
360034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 0;
360134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
360234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
3603819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else {
3604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
3605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
3606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3607819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                        "Unknown URI: " + uri + " match: " + match);
3608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
360910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            database.mNumDeletes++;
3610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
3611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
3613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3614a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
3615a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                if (sGetTableAndWhereParam.table.equals("files")) {
3616d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
3617d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    if (deleteparam == null || ! deleteparam.equals("false")) {
3618d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        database.mNumQueries++;
3619d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
3620d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sMediaTypeDataId,
3621d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
3622d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        String [] idvalue = new String[] { "" };
3623d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        while (c.moveToNext()) {
3624d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            int mediatype = c.getInt(0);
3625d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            if (mediatype == FileColumns.MEDIA_TYPE_IMAGE) {
3626d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                try {
3627d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Libcore.os.remove(c.getString(1));
3628d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    idvalue[0] =  "" + c.getLong(2);
3629d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    database.mNumQueries++;
3630d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Cursor cc = db.query("thumbnails", sDataOnlyColumn,
3631d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                            "image_id=?", idvalue, null, null, null);
3632d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    while (cc.moveToNext()) {
3633d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                        Libcore.os.remove(cc.getString(0));
3634d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    }
3635d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    cc.close();
3636d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    database.mNumDeletes++;
3637d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    db.delete("thumbnails", "image_id=?", idvalue);
3638d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                } catch (ErrnoException e) {
3639a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                                }
3640d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_VIDEO) {
3641d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                try {
3642d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Libcore.os.remove(c.getString(1));
3643d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                } catch (ErrnoException e) {
3644d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                }
3645d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_AUDIO) {
36465118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen                                idvalue[0] =  "" + c.getLong(2);
36475118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen                                db.delete("audio_genres_map", "audio_id=?", idvalue);
36485118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen                                db.delete("audio_playlists_map", "audio_id=?", idvalue);
3649d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_PLAYLIST) {
3650d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // TODO, maybe: remove the audio_playlists_cleanup trigger and implement
3651d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // it functionality here (clean up the playlist map)
36525afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen                            }
3653a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                        }
3654d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        c.close();
3655a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    }
3656a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                }
3657a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
3658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
365936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    case MTP_OBJECTS:
3660e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood                    case MTP_OBJECTS_ID:
3661d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        try {
3662d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // don't send objectRemoved event since this originated from MTP
3663d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = true;
366410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            database.mNumDeletes++;
366510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            count = db.delete("files", sGetTableAndWhereParam.where, whereArgs);
3666d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        } finally {
3667d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = false;
3668d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        }
366936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        break;
367078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    case AUDIO_GENRES_ID_MEMBERS:
367110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
367278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        count = db.delete("audio_genres_map",
367378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
367478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        break;
3675166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
3676a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS_ID:
3677a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS:
3678166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS_ID:
3679166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS:
3680166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        // Delete the referenced files first.
3681166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
3682166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sDataOnlyColumn,
3683166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
3684166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        if (c != null) {
3685166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            while (c.moveToNext()) {
3686166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                try {
3687166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                    Libcore.os.remove(c.getString(0));
3688166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                } catch (ErrnoException e) {
3689166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                }
3690166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            }
3691166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            c.close();
3692166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        }
3693166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        database.mNumDeletes++;
3694166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        count = db.delete(sGetTableAndWhereParam.table,
3695166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
3696166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        break;
3697166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
3698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
369910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
3700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
3701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
3702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
3703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
37043631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // Since there are multiple Uris that can refer to the same files
37053631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // and deletes can affect other objects in storage (like subdirectories
37063631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // or playlists) we will notify a change on the entire volume to make
37073631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // sure no listeners miss the notification.
37083631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                String volume = uri.getPathSegments().get(0);
37093631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume);
37103631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                getContext().getContentResolver().notifyChange(notifyUri, null);
3711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
3715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
371838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    public Bundle call(String method, String arg, Bundle extras) {
371938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (MediaStore.UNHIDE_CALL.equals(method)) {
372038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processRemovedNoMediaPath(arg);
372138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            return null;
372238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
372338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        throw new UnsupportedOperationException("Unsupported call: " + method);
372438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
372538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
372638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    @Override
3727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
3728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
3729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3730b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
3731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
373210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
373310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
3734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
373710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumUpdates++;
373810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
373910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
3740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3741b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
3742b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3743b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3744b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
3745b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3746b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
3748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
37501d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // special case renaming directories via MTP.
37511d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // in this case we must update all paths in the database with
37521d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // the directory name as a prefix
37531d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID)
37541d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    && initialValues != null && initialValues.size() == 1) {
37551d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                String oldPath = null;
3756801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA);
37577f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.remove(newPath);
37581d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                // MtpDatabase will rename the directory first, so we test the new file name
375938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(newPath);
376038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (newPath != null && f.isDirectory()) {
376110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumQueries++;
37621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION,
37631d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        userWhere, whereArgs, null, null, null);
37641d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    try {
37651d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null && cursor.moveToNext()) {
37661d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            oldPath = cursor.getString(1);
37671d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
37681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    } finally {
37691d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null) cursor.close();
37701d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
37711d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    if (oldPath != null) {
37727f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                        mDirectoryCache.remove(oldPath);
37731d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        // first rename the row for the directory
377410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
37751d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        count = db.update(sGetTableAndWhereParam.table, initialValues,
37761d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
37771d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0) {
37781d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            // then update the paths of any files and folders contained in the directory.
3779dc025ab2edc8d74170ae54dd33860adcca3ad31cMarco Nelissen                            Object[] bindArgs = new Object[] {oldPath + "/%", oldPath.length() + 1, newPath};
378010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
3781dc025ab2edc8d74170ae54dd33860adcca3ad31cMarco Nelissen                            db.execSQL("UPDATE files SET _data=?3||SUBSTR(_data, ?2) WHERE _data LIKE ?1;", bindArgs);
37821d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
37831d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
37841d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0 && !db.inTransaction()) {
37851d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            getContext().getContentResolver().notifyChange(uri, null);
37861d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
378738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        if (f.getName().startsWith(".")) {
378838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            // the new directory name is hidden
378938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            processNewNoMediaPath(helper, db, newPath);
379038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        }
37911d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        return count;
37921d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
379338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
379438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    processNewNoMediaPath(helper, db, newPath);
37951d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                }
37961d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            }
37971d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
3798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
3799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
3800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
3801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
3802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
38032658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
38042658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
38052658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        values.remove(MediaStore.Audio.Media.COMPILATION);
380607656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen
3807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
3808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
3809a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
38106006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("artist");
3811a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
3812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
381310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> artistCache = helper.mArtistCache;
3814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
3815a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
3816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
381710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    artistRowId = getKeyIdForName(helper, db,
381810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "artists", "artist_key", "artist",
3819acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
3820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
3821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
3822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
3823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
3824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
3825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
382759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
3828a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
38296006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("album");
3830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
3831801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                            String path = values.getAsString(MediaStore.MediaColumns.DATA);
383259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
38332658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            if (albumartist != null) {
38342658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                albumHash = albumartist.hashCode();
38352658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            } else if (compilation != null && compilation.equals("1")) {
38362658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                // nothing to do, hash already set
383759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
38389289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path == null) {
38399289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    if (match == AUDIO_MEDIA) {
38409289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Log.w(TAG, "Possible multi row album name update without"
38419289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                + " path could give wrong album key");
38429289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    } else {
38439289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        //Log.w(TAG, "Specify path to avoid extra query");
38449289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Cursor c = query(uri,
38459289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                new String[] { MediaStore.Audio.Media.DATA},
38469289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                null, null, null);
38479289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        if (c != null) {
38489289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            try {
38499289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                int numrows = c.getCount();
38509289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                if (numrows == 1) {
38519289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    c.moveToFirst();
38529289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    path = c.getString(0);
38539289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                } else {
38549289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    Log.e(TAG, "" + numrows + " rows for " + uri);
38559289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                }
38569289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            } finally {
38579289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                c.close();
38589289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            }
38599289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        }
38609289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    }
38619289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
38629289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path != null) {
38639289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
38649289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
386559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
38662658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
3867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
3868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
386910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> albumCache = helper.mAlbumCache;
3870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
387159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
387259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
3873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
387410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    albumRowId = getKeyIdForName(helper, db,
387510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "albums", "album_key", "album",
3876a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
3877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
3878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
3879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
3880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
3881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
3882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
3885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
3886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
3887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
3888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
3889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
3890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
3891e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
3892e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
3893e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
3894e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
3895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
389710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
3898afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        count = db.update(sGetTableAndWhereParam.table, values,
3899afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
3900b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        if (genre != null) {
3901b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            if (count == 1 && match == AUDIO_MEDIA_ID) {
3902b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                long rowId = Long.parseLong(uri.getPathSegments().get(3));
3903b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                updateGenre(rowId, genre);
3904b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            } else {
3905b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                // can't handle genres for bulk update or for non-audio files
3906b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                Log.w(TAG, "ignoring genre in update: count = "
3907b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                        + count + " match = " + match);
3908b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            }
3909b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        }
3910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
3913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
3914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
3915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
3916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
3917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
3918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
3919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
3920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
3921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
3922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
3923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
3924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
3925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
3926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
3927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3928b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                        computeTakenTime(values);
392910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
3930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
3931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
393201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
393301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
393401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
393501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
393610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumQueries++;
393701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
393801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
393901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
3940b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
3941216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                try {
3942216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    while (c.moveToNext()) {
3943216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        long magic = c.getLong(2);
3944216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        if (magic == 0) {
3945216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                            requestMediaThumbnail(c.getString(1), uri,
3946216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                                    MediaThumbRequest.PRIORITY_NORMAL, 0);
3947216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        }
3948b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
3949216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                } finally {
3950216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    c.close();
3951b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
3952b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
3953b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
3954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3956f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
3957f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3958f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
3959f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
3960f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
3961f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
3962f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
3963f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
3964f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
3965f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
396610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            return movePlaylistEntry(helper, db, playlist, oldpos, newpos);
3967f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
3968f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
3969f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
3970f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
3971f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
3972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
397310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumUpdates++;
3974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
3975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
3976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3979cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
3980cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
3981cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
3982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
3983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
3985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
398710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db,
398810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            long playlist, int from, int to) {
3989f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
3990f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
3991f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
3992f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
3993f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
3994f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            int numlines = 0;
399510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates += 3;
3996f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
3997f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=" + from +
3998f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
3999f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
4000f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
4001f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
4002f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
4003f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
4004f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order<=" + to + " AND play_order>" + from +
4005f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4006f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
4007f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
4008f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
4009f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order>=" + to + " AND play_order<" + from +
4010f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4011f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
4012f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
4013f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to +
4014f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
4015f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
4016f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
4017f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
4018f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            getContext().getContentResolver().notifyChange(uri, null);
4019f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return numlines;
4020f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
4021f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
4022f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
4023f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
4024f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
4026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
4027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
4028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
4030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
4031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
403271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
403471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
403571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
403671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
403771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
403871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
403971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
404071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
404171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
40425fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            if (db == null) {
40435fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                throw new IllegalStateException("Couldn't open database for " + uri);
40445fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            }
404571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
404671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
404771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
404871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
404971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
405071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
405171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
405271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
405371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
405471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
405571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
405671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
405771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
405871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
405971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
406071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
406171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
40623fa7593ce394cdaad4a3db622d415fd8497f4a9dMarco Nelissen                    pfd = openFileHelper(newUri, mode);
406371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
406471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
406571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, null);
406671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
406771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
406871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
406971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
407071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
407171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd = openFileHelper(uri, mode);
4074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
407571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
407671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
407771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
407871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
407971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
4081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
4082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
4083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
4084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
4085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
40875fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                if (db == null) {
40885fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                    throw new IllegalStateException("Couldn't open database for " + uri);
40895fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                }
4090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
409271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
4093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
4094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
4095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
4096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
4097a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen                        null, null, null, null, MediaStore.Audio.Media.TRACK);
409871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
4099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
410071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, uri);
4101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
4103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
410471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
410571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
410671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
4107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
4109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
411210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper;
4113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
4114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
4115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
4116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
4117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
411910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db,
412010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String path, long album_id) {
41218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
41228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
41238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
41248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
41258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
41268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
41278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
41298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
41308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
4131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
413210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        d.helper = helper;
4133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
4134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
4135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
4136a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
41378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
41398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
41408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
41418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
41428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
41438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
41448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
41458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
41468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
4148b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
4149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
4150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
41528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
41538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
41548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
41558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
4156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
4159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
4160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
4161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
41628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
41638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
4164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
4165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4166d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
41673f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
41683f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
4169d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
4170d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
4171d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
4172d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
4173d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
4174d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
41758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
4176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
4177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
4178d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
41793f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
41809be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    String sdroot = mExternalStoragePaths[0];
41810fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
41822f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
4183d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4184d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
4185d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
4186d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
4187d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
4188ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                        } else if (!artPath.equalsIgnoreCase(sdroot) &&
4189ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                                !artPath.equalsIgnoreCase(dwndir)) {
4190d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
4191d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
4192d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
4193d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
4194d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4195d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
4196d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
4197d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
4198d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
4199d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
4200d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4201d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
4202d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
4203d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
4204d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
4205d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4206d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
4207d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
4208d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
4209d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
4210d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4211d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
4212d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
4213d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4214d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
4215d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
4216d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4217d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
4218d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4219d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4220d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
4221d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
4222d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
4223d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
4224d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4225d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
42263f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
4227d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
4228d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            compressed = new byte[(int)file.length()];
4229d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
4230d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
4231d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
4232d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
4233d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
4234d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
4235d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
4236d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
4237d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
4238d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
42448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
42458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
4246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
42478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
42488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
4249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
42508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
425110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) {
42528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
42538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
42548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
42558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
4256801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA },
42578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
4258d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            try {
4259d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null && c.moveToFirst()) {
4260d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    String albumart_path = c.getString(0);
4261d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    if (ensureFileExists(albumart_path)) {
4262d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                        out = albumart_uri;
4263d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    }
4264d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                } else {
4265d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    albumart_uri = null;
4266d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                }
4267d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            } finally {
4268d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null) {
4269d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    c.close();
4270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
427271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
427371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
42748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
42758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
42768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
42778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
427810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
4279801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
42808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
42818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
4282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
42838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
42848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
42858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
42868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
42878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
42888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
42898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
42908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
42918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
42928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
42938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) {
42948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean success = false;
42958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
42968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            OutputStream outstream = getContext().getContentResolver().openOutputStream(out);
42978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
42988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
42998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
43008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
43018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
43028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = true;
43038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
430470676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann                success = bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream);
4305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
43068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
43078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            outstream.close();
43088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (FileNotFoundException ex) {
43098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
4310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (IOException ex) {
43118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
43128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
43138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (!success) {
43148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // the thumbnail was not written successfully, delete the entry that refers to it
43158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            getContext().getContentResolver().delete(out, null, null);
4316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
43178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
43188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
431971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor getThumb(SQLiteDatabase db, String path, long album_id,
432071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Uri albumart_uri) {
432171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
432271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
432371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
432471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
432571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
432671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
432771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
432871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
432971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
43308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
4331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
43328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
433371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
43348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
43358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
43368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
43378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
43388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
43398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
43408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
43418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
43428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
43438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
43448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
43458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
43468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
434770676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final Resources r = getContext().getResources();
434870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size);
434970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) {
43508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
43518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
43528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
43538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
43548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
43558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
43568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
43578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
43588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
43598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
43608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
43618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
43628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
43638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
43648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
43658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
4366a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
4367a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
4368a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
4369a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
4370a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
43718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
43728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
43738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
43748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
43758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
43768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
437771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
43788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
43798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
438071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
438171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
438271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
438371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
438424001394f571b1f0378840cbf299288e4df10508Bjorn Bringert                return ParcelFileDescriptor.fromData(compressed, "albumthumb");
438571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
438671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
438771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
4388a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
4389a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
4390a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
4391a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
4392a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
4393a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
439410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Uri out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
4395a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
4396a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
4397a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
4398a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
4399a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    ParcelFileDescriptor pfd = openFileHelper(out, "r");
4400a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
4401a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
4402a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
4403a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (FileNotFoundException ex) {
4404a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4405a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
4406a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4407a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
4408a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
4409a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
4410a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
441171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
441271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
44138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
441471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
4415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
4419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
4420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
4421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
4422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
4423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
4424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
442559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
442659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
442759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
4428a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
4429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
4430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
4431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
4432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
4433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
443410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db,
443510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String table, String keyField, String nameField,
443659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
4437a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
4438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
4439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
444151cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            rawName = MediaStore.UNKNOWN_STRING;
4442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
4444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
444651cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            // shouldn't happen, since we only get null keys for null inputs
444751cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            Log.e(TAG, "null key", new Exception());
4448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
4449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
445159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
4452e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
445359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
44542658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // To distinguish same-named albums, we append a hash. The hash is based
44552658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // on the "album artist" tag if present, otherwise on the "compilation" tag
44562658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // if present, otherwise on the path.
445759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
445859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
445959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
446059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
446159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
4462a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
446359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
4464a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
4465a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
4466a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
446759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
446859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
4469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
447010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
4471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
4472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
4475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
4476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
4477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
4478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
4479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
448010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumInserts++;
4481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
448259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
4483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
448410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            makeThumbAsync(helper, db, path, rowId);
4485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
4487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
4494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
4495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
4496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
4497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
4499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
4500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
4501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
4502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
4503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
4504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
4505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
450610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
4507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
4508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
4515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
4516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
4517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
4518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
4521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
4522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
452459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
452559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
4526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
4528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
4532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
4533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
4534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
4535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
4536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
4538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
4539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
4540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
4541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
4543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
4544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
4545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
4547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
4548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
4550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
4551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
4552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
4553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
4554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
4555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
4556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
4557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
4558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
4562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
4563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
4564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
4565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
4566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
4567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
4570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
4571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
4576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
4578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
4579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
4581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
4582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (uri.getPathSegments().size() > 1) {
4583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
4584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
4587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4589fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isMediaDatabaseName(String name) {
4590fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
4591fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4592fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4593fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (EXTERNAL_DATABASE_NAME.equals(name)) {
4594fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4595fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4596fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (name.startsWith("external-")) {
4597fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4598fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4599fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
4600fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
4601fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
4602fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isInternalMediaDatabaseName(String name) {
4603fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
4604fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4605fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4606fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
4607fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
4608fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
4609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
4611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
4612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
4613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
4615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
4616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
461875392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
4619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
4620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
4621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
4624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
4625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
4626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4628993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            Context context = getContext();
462910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper helper;
4630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
463110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true,
4632fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                        false, mObjectRemovedCallback);
4633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
4634993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                if (Environment.isExternalStorageRemovable()) {
46359be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    String path = mExternalStoragePaths[0];
4636993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    int volumeID = FileUtils.getFatVolumeId(path);
4637993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
4638993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
4639993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // generate database name based on volume ID
4640993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
464110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbName, false,
4642fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
4643993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    mVolumeId = volumeID;
4644993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                } else {
4645993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // external database name should be EXTERNAL_DATABASE_NAME
4646993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // however earlier releases used the external-XXXXXXXX.db naming
4647993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // for devices without removable storage, and in that case we need to convert
4648993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // to this new convention
4649993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME);
4650993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (!dbFile.exists()) {
4651993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // find the most recent external database and rename it to
4652993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // EXTERNAL_DATABASE_NAME, and delete any other older
4653993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // external database files
4654993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        File recentDbFile = null;
4655993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        for (String database : context.databaseList()) {
4656993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (database.startsWith("external-")) {
4657993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                File file = context.getDatabasePath(database);
4658993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                if (recentDbFile == null) {
4659993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
4660993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else if (file.lastModified() > recentDbFile.lastModified()) {
4661993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile.delete();
4662993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
4663993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else {
4664993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    file.delete();
4665993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                }
4666993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
4667993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
4668993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        if (recentDbFile != null) {
4669993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (recentDbFile.renameTo(dbFile)) {
4670993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.d(TAG, "renamed database " + recentDbFile.getName() +
4671993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
4672993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            } else {
4673993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.e(TAG, "Failed to rename database " + recentDbFile.getName() +
4674993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
4675993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // This shouldn't happen, but if it does, continue using
4676993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // the file under its old name
4677993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                dbFile = recentDbFile;
4678993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
4679993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
4680993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME
4681993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    }
468210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbFile.getName(), false,
4683fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
4684993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                }
4685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
4686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
4687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
468910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            mDatabases.put(volume, helper);
4690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
469110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (!helper.mInternal) {
4692ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                // create default directories (only happens on first boot)
469310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                createDefaultFolders(helper, helper.getWritableDatabase());
4694ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
4695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
46969be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles();
4697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
4698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
4699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
4700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
4703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
4704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
4705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
4706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
4707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
4709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
4710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
4713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
4714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
4715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
4716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
4717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
4722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
4723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
4727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
4728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
4729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
4731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
473375392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
4734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
4735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
4736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
4739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
4740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
4741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
4742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
4743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
4744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
4745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
4748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
4749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
4750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
4752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
4753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
4754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
4755e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood            } catch (Exception e) {
4756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
4757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
4760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
4761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
4764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
4765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
4768ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer    private static final boolean LOCAL_LOGV = false;
4769971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn
4770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
4771993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private static final String EXTERNAL_DATABASE_NAME = "external.db";
4772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
4774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
4775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
4777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
4778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
4779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
4781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
4783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
4785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
4786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
47870027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
4788993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private int mVolumeId = -1;
47890027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
4790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
4791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
4792268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
4793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
4795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
4796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
47971717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
479816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    // are stored in the "files" table, so do not renumber them unless you also add
47991717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // a corresponding database upgrade step for it.
4800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
4801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
4802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
4803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
4804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
4806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
4807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
4808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
4809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
4810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
4811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
4812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
4813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
4814bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen    private static final int AUDIO_GENRES_ALL_MEMBERS = 109;
4815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
4816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
4817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
4818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
4819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
4820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
4821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
4822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
4823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
4824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
4825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
482671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
4827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
4829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
4830b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
4831b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
4832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
4834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
4835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4836a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
4837a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
4838a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
4839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
4841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
48420027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
4843704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen    private static final int VERSION = 601;
48440027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
484516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES = 700;
484616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES_ID = 701;
4847a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
4848e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    // Used only by the MTP implementation
4849e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS = 702;
4850e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS_ID = 703;
4851e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECT_REFERENCES = 704;
4852819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // UsbReceiver calls insert() and delete() with this URI to tell us
4853819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // when MTP is connected and disconnected
4854819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    private static final int MTP_CONNECTED = 705;
4855b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
4856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
4857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
4858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4859b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
4860b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
4861b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
4862b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
48631d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    private static final String[] PATH_PROJECTION = new String[] {
48641d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood        MediaStore.MediaColumns._ID,
48651d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            MediaStore.MediaColumns.DATA,
48661d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    };
48671d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
4868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
4869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
4870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
4871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
4872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4873b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
4874b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
4875b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
4876b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
4877b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
4878b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
4879e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final String OBJECT_REFERENCES_QUERY =
4880afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map"
4881afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?"
4882afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER;
4883e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
4884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
4885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
4886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
4887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
4888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
4889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
4890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
4892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
4893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
4894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
4895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
4896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
4897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
4898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
4899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
4900bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen        URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS);
4901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
4902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
4903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
4904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
4905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
4906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
4907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
4908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
4909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
4910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
4911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
491271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
4913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
4915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
4916b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
4917b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
4918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
4920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
49210027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
4922704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        URI_MATCHER.addURI("media", "*/version", VERSION);
49230027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
4924819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED);
4925819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
4926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
4927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
4928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4929b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        // Used by MTP implementation
493016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file", FILES);
493116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file/#", FILES_ID);
4932e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS);
4933e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID);
4934e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES);
4935b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
4936a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
4937a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
4938a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
4939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
4940a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
4941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
4942a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
4943a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
4944a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
4945a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
4946a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
4947a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
4948a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
4949a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
4950a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
4951a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
4952a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
4953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
495410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
495510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    @Override
495610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
495710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        Collection<DatabaseHelper> foo = mDatabases.values();
495810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        for (DatabaseHelper dbh: foo) {
495910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            StringBuilder s = new StringBuilder();
496010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            s.append(dbh.mName);
496110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            s.append(": ");
496210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            SQLiteDatabase db = dbh.getReadableDatabase();
496310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (db == null) {
496410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append("null");
496510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
496610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append("version " + db.getVersion() + ", ");
496710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null);
496810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                try {
496910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (c != null && c.moveToFirst()) {
497010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        int num = c.getInt(0);
497110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append(num + " rows, ");
497210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
497310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append("couldn't get row count, ");
497410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
497510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                } finally {
497610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (c != null) {
497710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        c.close();
497810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
497910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
498010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumInserts + " inserts, ");
498110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumUpdates + " updates, ");
498210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumDeletes + " deletes, ");
498310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumQueries + " queries, ");
498410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (dbh.mScanStartTime != 0) {
498510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    s.append("scan started " + DateUtils.formatDateTime(getContext(),
498610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            dbh.mScanStartTime / 1000,
498710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            DateUtils.FORMAT_SHOW_DATE
498810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            | DateUtils.FORMAT_SHOW_TIME
498910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            | DateUtils.FORMAT_ABBREV_ALL));
499010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (dbh.mScanStopTime < dbh.mScanStartTime) {
499110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append(" (ongoing)");
499210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
499310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append(" (" + DateUtils.formatElapsedTime(
499410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                (dbh.mScanStopTime - dbh.mScanStartTime) / 1000000) + ")");
499510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
499610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
499710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
499810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            writer.println(s);
499910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
500010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (mMediaScannerVolume != null) {
500110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            writer.println("Scanning: " + mMediaScannerVolume);
500210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
500310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    }
5004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
5005